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

Once, I had faced a strange issue with one of my custom workflow which I had developed using Visual Studio. I had several tasks that get created during the workflow and so I added the task creation/completion logic to a Custom Activity. The task has a custom aspx page associated with it (instead of an InfoPath form). When changes are made to my task, I call SPWorkflowTask.AlterTask within the aspx code to change the status of the task from "Not Started", to either “In Progress” or “Completed”. At that time, when this method is called, it locks the workflow and throws below error.

SPException: This task is currently locked by a running workflow and cannot be edited”

Root cause:   issue has occurred whenever a task has been altered. If a workflow is ‘waiting’ for an event from this task, it will be ‘locked’ and unchangeable until the workflow has a change to process this event.

Solution (work-around 1): Using the below code snippet you can find out the workflow status and can do the necessary action.

SPWorkflowCollection wfs = item.Workflows;

foreach(SPWorkflow wf in wfs)
{
if ( wf.InternalState.ToString() != "Locked, Running")
{
       // alter the task here
}
}

If we are trying to change the status of workflow from outside the workflow this error will throw, In this case I had used a custom aspx page to do that. So, in order to avoid that locking exception, before changing the status find out the status of the workflow whether it is locked status or not and make the changes.

Solution (work-around 2): Make the alteration task not synchronous and make the NonSerialaized SP objects

If we use some objects like SPWeb,SPUser, it has to be NonSerializable. So we have to make those type of objects in the SharePoint workflow code as NonSerializable by adding the [NonSerialaized()] attribute on top of the declarations.

Eg:if you are using a SPWeb and SPUser as like this

private SPWeb oWeb = null;

private SPUser oUser = null;

then you can make those objects as NonSerialized like below,

[NonSerialized()]

private SPWeb oWeb = null;

[NonSerialized()]

private SPUser oUser = null;

Also, one more important change you have to make is synchronization = false while altering the task.

if we alter the task like this SPWorkflowTask.AlterTask(oTaskListItem, oTaskHash, true);, the third parameter represents a Boolean variable denotes synchronization is true or false. If we make it as true, it will make immediate modification on task (it mightcreate this locking issue) and if make it as false then it will depend on the timer activity and later it will be modified.

So if we get this error message, make the synchronization parameter as false. like below

SPWorkflowTask.AlterTask(oTaskListItem, oTaskHash, false);