SharePoint Internals – Hristo Pavlov’s Blog

14 May, 2008

Uploading a file & Event Receivers: The file … has been modified by …

Filed under: SharePoint — Tags: , , , , — hristopavlov @ 1:20 am

This error occurs when you upload a file to a document library through the SharePoint UI and when the document library has an event receiver which updates the list item on ItemAdded(). The error doesn’t happen all the time which lead us to conclude that it is due to some race conditions, especially knowing that event receivers are run in separate thread.

The error message is “The file … has been modified by … on ….” and the callstack leads to a COMException in the Microsoft.SharePoint.Library assembly, which is the managed wrapper of OWSSVR.dll. The full stacktrace looks something like this:

[COMException (0x81020037): The file Administration/test.doc has been modified by SHAREPOINT\system on 12 May 2008 15:14:29 +1000.]
at Microsoft.SharePoint.Library.SPRequestInternalClass.AddOrUpdateItem(String bstrUrl, String bstrListName, Boolean bAdd, Boolean bSystemUpdate, Boolean bPreserveItemVersion, Boolean bUpdateNoVersion, Int32& plID, String& pbstrGuid, Guid pbstrNewDocId, Boolean bHasNewDocId, String bstrVersion, Object& pvarAttachmentNames, Object& pvarAttachmentContents, Object& pvarProperties, Boolean bCheckOut, Boolean bCheckin, Boolean bMigration, Boolean bPublish) +0
at Microsoft.SharePoint.Library.SPRequest.AddOrUpdateItem(String bstrUrl, String bstrListName, Boolean bAdd, Boolean bSystemUpdate, Boolean bPreserveItemVersion, Boolean bUpdateNoVersion, Int32& plID, String& pbstrGuid, Guid pbstrNewDocId, Boolean bHasNewDocId, String bstrVersion, Object& pvarAttachmentNames, Object& pvarAttachmentContents, Object& pvarProperties, Boolean bCheckOut, Boolean bCheckin, Boolean bMigration, Boolean bPublish) +411

[SPException: The file Administration/test.doc has been modified by SHAREPOINT\system on 12 May 2008 15:14:29 +1000.]
at Microsoft.SharePoint.Library.SPRequest.AddOrUpdateItem(String bstrUrl, String bstrListName, Boolean bAdd, Boolean bSystemUpdate, Boolean bPreserveItemVersion, Boolean   bUpdateNoVersion, Int32& plID, String& pbstrGuid, Guid pbstrNewDocId, Boolean bHasNewDocId, String bstrVersion, Object& pvarAttachmentNames, Object& pvarAttachmentContents, Object& pvarProperties, Boolean bCheckOut, Boolean bCheckin, Boolean bMigration, Boolean bPublish) +556
at Microsoft.SharePoint.SPListItem.AddOrUpdateItem(Boolean bAdd, Boolean bSystem, Boolean bPreserveItemVersion, Boolean bNoVersion, Boolean bMigration, Boolean bPublish, Boolean bCheckOut, Boolean bCheckin, Guid newGuidOnAdd, Int32& ulID, Object& objAttachmentNames, Object& objAttachmentContents, Boolean suppressAfterEvents) +3030
at Microsoft.SharePoint.SPListItem.UpdateInternal(Boolean bSystem, Boolean bPreserveItemVersion, Guid newGuidOnAdd, Boolean bMigration, Boolean bPublish, Boolean bNoVersion, Boolean bCheckOut, Boolean bCheckin, Boolean suppressAfterEvents) +632
at Microsoft.SharePoint.SPListItem.UpdateOverwriteVersion() +190
at Microsoft.SharePoint.WebControls.SaveButton.SaveItem(SPContext itemContext, Boolean uploadMode, String checkInComment) +256
at Microsoft.SharePoint.WebControls.SaveButton.SaveItem() +111
at Microsoft.SharePoint.WebControls.SaveButton.OnBubbleEvent(Object source, EventArgs e) +476
at System.Web.UI.Control.RaiseBubbleEvent(Object source, EventArgs args) +50
at System.Web.UI.Page.RaisePostBackEvent(IPostBackEventHandler sourceControl, String eventArgument) +39
at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +3215

I spent some time writing test code to try and reproduce the problem when multiple threads are doing updates to the same file and I finally managed to get the error. Then I isolates the cause and reworked the code so the exception is thrown even from a single thread. I finally ended up with the code below which always causes the error. It is running in a single thread but it represents exactly what is happening with the event receiver and the EditForm.aspx pages if you think of it this way: All objects with index “1” are accessed from the event receiver and all objects with index “2”are accessed from the EditForm.aspx code-behind.

using (SPSite site1 = new SPSite(http://server/sites/test/”))

using (SPSite site2 = new SPSite(http://server/sites/test/”))

{

      SPFilefile1 = site1.OpenWeb().Lists[“ListName”].RootFolder.Files[0];

      SPFilefile2 = site2.OpenWeb().Lists[“ListName”].RootFolder.Files[0];

 

      // It is important to read a field value

      object readValue2 = file2.Item[“FieldName”];

 

      file1.Item.UpdateOverwriteVersion();

 

 

     // The next line throws exception

      file2.Item.UpdateOverwriteVersion();

}

The issue only happens when the code is executed in the exact sequence listed above. There is no exception thrown if we first update item2 and then item1. It doesn’t happen if we read a value from item1 rather than item2 and it doesn’t happen if we don’t read any value at all. It also happens no matter if we are using one or two different SPSite objects. All this explains why the issue is not happening all the time but only when specific race conditions are met.

The next question is of course how do we prevent the problem from happening. Well we cannot change  the SharePoint code so we have to find a way to update our event receiver code (which is the one with index “1” above). Also note that the EditForm.aspx uses exactly the SPListItem.UpdateOverwriteVersion()method to do the item metadata update and this cannot be changed either.

There are four ways to uppdate a list item through the object model. All of them call the internal method UpdateInternal() which has some interesting arguments like bSystem, bPreserveItemVersion and bNoVersion. The table below shows the different ways to update a list item and how they call the UpdateInternal() method:

Method   bSystem     bPreserveItemVersion    bNoVersion
SystemUpdate() true false false
SystemUpdate(false) true true false
UpdateOverwriteVersion() false false true
Update() false false false

The experiment showed that all of them but one were leading to an exception being thrown. The method that worked was SystemUpdate(false).

So one way to fix the problem is changing your event receiver to update the list item using SystemUpdate(false). Some people are additionally recommending to disable the event firing when calling the update as well:

this.DisableEventFiring();

try

{

      itemToUpdate.SystemUpdate(false);

}

finally

{

      this.EnableEventFiring();

}

This will work well unless you actually want to create a new version of the list item or call any of the other update methods for whateever reason. If that’s your case you could try to use a workaround of delaying the event handler for a couple of seconds (using Thread.Sleep() ) which of course is far from an elegant solution or you could see whether you can move your code to ItemUpdated() or ItemUpdating() rather than ItemAdded().

If you are getting the error in a different case i.e. when your custom code is where the error is thrown (in the case described here the error is thrown in the SharePoint code) you could implement a workaround of waiting and retrying your action again and again after a few seconds for a couple times. As an implementation you could use a try – catch block with a goto statement that moves the execution before the try. This is a very legitimate and good example of when using a “goto” statement in your code is actually good. If you don’t agree with me about the “goto” statement then look at this different but similar problem caused by race conditions and how it is solved using a try-catch-goto block in this Microsoft article: http://msdn.microsoft.com/en-us/library/cc303696.aspx

Usually if you are getting the error in your own code it is due to an SPSite created in a completely different branch of the solution and then SPWebs or SPLists are being passed as arguments down to your function where you try to update the list item. To resolve this error always remember to create a new fresh SPSite object to retrieve the list and the file objects from, in the same method where you are doing the update. This helps big time and solves 90% of the causes of this error I’ve seen. If needed you can additionally add more advanced thread synchronisation techniques to your code such as lock statements, mutexes, semaphors etc.

18 Comments »

  1. […] SharePoint Internals – Hristo Pavlov’s Blog wrote an interesting post today on Uploading a file & Event Receivers: The file … has been modified by …Here’s a quick excerpt … doc has been modified by SHAREPOINTsystem on 12 May 2008 15:14:29 +1000. ] at Microsoft. SharePoint. Library. SPRequest…. … doc has been modified by SHAREPOINTsystem on 12 May 2008 15:14:29 +1000. ] at Microsoft. SharePoint. Library. SPRequestInternalClass…. … objAttachmentContents, Boolean suppressAfterEvents) +3030 at Microsoft. SharePoint. SPListItem…. … pvarAttachmentNames, Object& pvarAttachmentContents, Object& pvarProperties, Boolean bCheckOut, Boolean bCheckin, Boolean bMigration, Boolean bPublish) +0 at Microsoft. SharePoint. Library. SPRequest…. […]

    Pingback by Microsoft » Blog Archive » Uploading a file & Event Receivers: The file … has been modified by … — 14 May, 2008 @ 3:21 am

  2. […] Adobe Blogs wrote an interesting post today on Uploading a file & Event Receivers: The file … has been modified…Here’s a quick excerptThis error occurs when you upload a file to a document library through the SharePoint UI and when the document library has an event receiver which […]

    Pingback by Library » Blog Archive » Uploading a file & Event Receivers: The file … has been modified… — 14 May, 2008 @ 3:23 am

  3. Thanks, this helped a bunch. Sharepoint documentation is less than stellar at the moment.

    Comment by Seth Flowers — 20 May, 2008 @ 3:05 pm

  4. Thanks for this post. It helped us solve a save conflict bug on the ItemAdded event. Stellar.

    Comment by Jake J — 23 May, 2008 @ 1:31 pm

  5. Thanks, this helped me to resolve the issue.

    Comment by Narendra — 30 October, 2008 @ 8:43 pm

  6. I have am getting the same error in a different scenario. I have a workflow on a picture library. When a new file or files are uploaded, the workflow process resizes the image. When “upload multiple pictures” is selected there is no problem. When the use selects a “Upload Picture” to load a single picture, the workflow completes its processing, then the EditForm.aspx page loads. When the user enters in any metadata and clicks Ok, the error you describe occurs. If the user then navigates back to the picture library, the new file has been uploaded, it is resized correctly, and the workflow history indicates the workflow completed successfully. I don’t have you depth of knowledge, so I’m only guessing that the editform has a reference to the new file before it was processed by thew workflow and the error occurs when it tries to save the file. Any ideas on a work-around? thanks

    Comment by Dan — 27 January, 2009 @ 7:01 pm

    • Hi Dan,

      Why not resizing the picture on ItemAdding event receiver rather than a workflow? Workflows run asynchronously but ItemAdding runs synchrounously. I believe this may solve your problem.

      Hristo.

      Comment by hristopavlov — 27 January, 2009 @ 9:07 pm

    • Its been a while since Dan posted this so I am sure he got his issue resolved. I am posting this link in case someone else has the same issue. This article helped me so it might help others getting this error resolved when having a workflowm .
      http://rehmangul.sauftveyr.com/2010/10/20/com-exception-while-adding-item-to-document-library/

      Comment by Aza — 2 February, 2012 @ 4:42 pm

  7. Unless I am missing something, I don’t have access to the SPListItem and SPFile during the ItemAdding event – so I can’t load the image from the file, process it, then save it back.

    Comment by Dan — 17 February, 2009 @ 7:56 pm

  8. Hi Dan,

    This is a good point I didn’t think about. Actually there are many issues caused by the fact that when you upload a file there are two pages that process this – Upload.aspx and the EditForm.aspx. I’m still working on my Event Receivers post and all those issues with Document Library event receivers are important part of it. So I am looking for ways to at least partially solve some of those problems. Check my incoming Event Receivers blogpost.

    Hristo.

    Comment by hristopavlov — 17 February, 2009 @ 10:38 pm

  9. Hi, Thanks for this post. It helped out with issues we were having relating to an email enabled document library what was running a workflow when the item was added to the library.

    Comment by Roberta — 9 April, 2009 @ 11:40 am

  10. […] InfoPath – The File Has Been Modified Race Condition Filed under: Uncategorized — sladescross @ 4:34 pm https://hristopavlov.wordpress.com/2008/05/14/uploading-a-file-event-receivers-the-file-has-been-modi… […]

    Pingback by InfoPath – The File Has Been Modified Race Condition « Sladescross's Blog — 11 November, 2009 @ 3:34 pm

  11. Hi, I am having ItemAdded eventreceiver which basically modify the properties of the document. The problem is when user upload a document through Upload.aspx, it redirects to EditForm.aspx. So if ItemAdded event is completed before EditForm.aspx appears, everything is fine. But if EditForm.aspx appears first, then ItemAdded changes the properties in the back-end and if user clicks on OK on EditForm.aspx, he gets the error saying The file has been modified. Any help ? Thanks in Advance.

    Comment by Manas — 4 December, 2009 @ 4:52 pm

  12. I have the same issue but I don’t want to solve this with a workflow.

    I wrote a Outlook Addin Script for saving emails directly to a sharepoint library. So i get an email, move this email to a special folder and my outlook addin will save this file in this special folder directly to the library.This works great, but sometimes I got the Error, that the File …. has been modified by…. and so on

    Well I use a ItemAdded eventhandler for reading out the email header and write this informations in the sharepoint columns (subject,from,to,received and so on).

    Sometimes it works well and sometimes I got the error and the listitem won’t be updated. So i decide to check, if Itemupdated will work, but if I simply upload my mail with my addin (simply saveas(Target-path) function) to sharepoint, there will no Itemupdated event fired so I can’t use my code in Itemupdated.

    Anyone who can help me?

    Thanks a lot

    Comment by gradma — 20 January, 2010 @ 10:37 am

  13. You need to properties.Web.AllowUnsafeUpdates = true;
    I’ve found it at http://www.eggheadcafe.com/software/aspnet/32158639/itemupdate-spexception–has-been-modified-by.aspx

    Comment by LW — 15 September, 2010 @ 7:58 am

  14. Hi,

    I’m trying to set up as suggested, but still I get error. The code is below, along with the logging entries:

    public override void ItemAdded(SPItemEventProperties properties)
    {
    Logger.Append(“Entering ItemAdded…”, Logger.INFO);

    try
    {
    SPList list = properties.ListItem.ParentList;
    SPListItem theItem = properties.ListItem;

    // Only proceed if item has not already been updated.
    //if (!ItemAlreadyUpdated(theItem))
    //{
    //Logger.Append(“Item not updated.”, Logger.INFO);

    string listName = list.Title;
    string projectName = string.Empty;
    try
    {
    projectName = listName;
    }
    catch { }

    string docID = string.Empty;
    int rowsAffected = -1;
    Logger.Append(“Getting connectionstring…”, Logger.INFO);
    string connectionString = GetConnectionString();

    if (connectionString != string.Empty)
    {
    Logger.Append(“Got connectionstring: ” + connectionString, Logger.INFO);

    GetDocumentID(projectName, ref docID, ref rowsAffected, connectionString);

    Logger.Append(“Got DocumentID: ” + docID, Logger.INFO);
    }
    else
    {
    Logger.Append(“Error getting DocumentID”, Logger.ERROR);
    }

    if (docID != string.Empty && rowsAffected > 0 && listName != string.Empty)
    {
    try
    {
    base.DisableEventFiring();

    InsertDocumentID(theItem, listName, docID);
    }
    catch (Exception err)
    {
    Logger.Append(“An error occured while trying to update the document”, Logger.ERROR);
    throw;
    }
    finally
    {
    base.EnableEventFiring();
    }
    }
    else
    {
    Logger.Append(“DocumentID not set. Skipping update…”, Logger.INFO);
    }
    }
    catch (Exception err)
    {
    Logger.Append(“Error occured: ” + err.StackTrace, Logger.ERROR);
    throw err;
    }
    finally
    {
    Logger.Append(“Leaving ItemAdded…”, Logger.INFO);
    base.ItemAdded(properties);
    }
    }

    private void InsertDocumentID(SPListItem theItem, string listName, string docID)
    {
    int retryCounter = 0;
    ReTry:
    try
    {

    theItem[DocumentIDFieldName] = docID;
    theItem[DocumentField] = listName;

    theItem.SystemUpdate(false);
    Logger.Append(“DocumentID updated on content type field with value ” + docID, Logger.INFO);
    }
    catch (Exception err)
    {
    // Handle the exception by retrying 5 times
    retryCounter += 1;
    if (retryCounter <= 5)
    {
    Logger.Append("Error occured. Retrying…", Logger.ERROR);
    // Wait for 2,5 seconds
    System.Threading.Thread.Sleep(2500);
    goto ReTry;
    }
    else
    {
    Logger.Append("Error updating content type fields. " + err.Message, Logger.ERROR);
    }
    }
    }

    Here's to log output when error occurs:

    09:21:40|Entering ItemAdded…
    09:21:40|Getting connectionstring…
    09:21:40|Got connectionstring: Data Source=.; Initial Catalog=****; User Id=****; Password=****;
    09:21:40|Got DocumentID: JOHNO0011101021
    09:21:40|Entering ItemUpdated…
    09:21:40|Error occured. Retrying…
    09:21:40|Nothing to do. Item NOT updated.
    09:21:40|Leaving ItemUpdated…
    09:21:42|Error occured. Retrying…
    09:21:45|Entering ItemUpdated…
    09:21:45|Nothing to do. Item NOT updated.
    09:21:45|Leaving ItemUpdated…
    09:21:45|Error occured. Retrying…
    09:21:48|Error occured. Retrying…
    09:21:49|Entering ItemUpdated…
    09:21:49|Nothing to do. Item NOT updated.
    09:21:49|Leaving ItemUpdated…
    09:21:50|Error occured. Retrying…
    09:21:53|Error updating content type fields. The file JOHNO001/ee.docx has been modified by DOMAIN\USERNAME on 16 jan 2011 21:21:45 +0100.
    09:21:53|Leaving ItemAdded…

    When everything is working, the log output looks like this:

    09:20:07|Entering ItemAdded…
    09:20:07|Getting connectionstring…
    09:20:07|Got connectionstring: Data Source=TS-SQL-04\A431275; Initial Catalog=CRMUtility; User Id=donald; Password=donald;
    09:20:07|Got DocumentID: IT0071101017
    09:20:07|DocumentID updated on content type field with value IT0071101017
    09:20:07|Leaving ItemAdded…
    09:20:07|Entering ItemUpdated…
    09:20:07|Item updated.
    09:20:07|Leaving ItemUpdated…
    09:20:09|Entering ItemUpdated…
    09:20:09|Item updated.
    09:20:09|Leaving ItemUpdated…
    09:20:13|Entering ItemUpdated…
    09:20:13|Item updated.
    09:20:13|Leaving ItemUpdated…

    The error only occurs the first time the event receiver runs after an IISReset or Recycling of the App Pool.

    I hope someone can help me with this, since I've now tried everything.

    Kind regards,

    Frank

    Comment by Frank — 16 January, 2011 @ 9:47 pm


RSS feed for comments on this post. TrackBack URI

Leave a comment

Create a free website or blog at WordPress.com.