Freigeben über


Errors when using loops in custom workflows

Custom workflows are always fun to develop and run, aren't they? Just not when they start throwing out error messages which you can't explain though ...
This could be the case with a workflow which contains a loop. The loop checks after each task update whether the task is set as completed or not. The issue? When a user tries to update the task for the second or third time, he gets the following error message:

 This task is currently locked by a running workflow and cannot be edited.

Needless to say that the error message persists even on the next day or week, which means that it's not just a temporary lock set by the workflow on the task item. Checking the stack trace for our exception, we can see that it occurs in PrepareItemForUpdate:

 internal void PrepareItemForUpdate(Guid newGuidOnAdd, SPWeb web, bool bMigration, out bool bAdd, ref bool bPublish, ref object objAttachmentNames, ref object objAttachmentContents, ref int parentFolderId);

The internal code checks for the field SPBuiltInFieldId.WorkflowVersion. If the field equals to 1, then the item update is carried on, if not, an exception is thrown saying that the item is locked by the task.
So what can be done?

1. You could change the code which gets executed automatically when you check the task for completion (most probably, this code part gets automatically called after each task item update performed by the users). If you find a value different than 1 for the SPBuiltInFieldId.WorkflowVersion field, you need to reset it to 1.

 static readonly string siteUrl = "https://testserver";
static readonly string listName = "Shared Documents";
static readonly int documentID = 2;
static readonly int workflowIndex = 0;

static void Main(string[] args) {
  using (SPSite ms = new SPSite(siteUrl))
  using (SPWeb mw = ms.OpenWeb()) {
    SPList ml = mw.Lists[listName];
    SPListItem li = ml.GetItemById(documentID);
    Console.WriteLine("No. of workflows: " + li.Workflows.Count);
    for (int i = 0; i < li.Workflows[workflowIndex].Tasks.Count; i++) {
      SPListItem ta = li.Workflows[workflowIndex].Tasks[i];
      Console.WriteLine("Task #" + i + ": " + ta.Title);
      Console.WriteLine("  Before: " + ta[SPBuiltInFieldId.WorkflowVersion]);
      if (ta[SPBuiltInFieldId.WorkflowVersion].ToString() != "1") {
        ta[SPBuiltInFieldId.WorkflowVersion] = 1;
        ta.SystemUpdate(false);
        Console.WriteLine("After: " + ta[SPBuiltInFieldId.WorkflowVersion]);
      } } } }

2. You could enable an additional event handler on the tasks list, and have the SPBuiltInFieldId.WorkflowVersion field updated to 1 on the ItemAdded and ItemUpdated events.