I have been working with uploading larger files lately with REST, and in an attempt to use concurrency control I stumbled on an annoying gotchya.

I received an error along the lines of

POST https://mysite.sharepoint.com/sites/tw-dev1/_api/Web/GetFileByServerRelativeUrl('/sites/tw-dev1/Documents/a04c54c1-bd17-4a58-b506-6dd31f94b623.txt')/ListItemAllFields 412 (Precondition Failed)

There are 2 issues I found:

  1. When creating a file using REST, the GUID included in the ETag response is in Upper Case but when you POST you need to use lower case
  2. The GUID may also contain braces { } which also need to be removed

Note: At some point in the past I remember the ETag just being a number, but now in SharePoint Online includes a format like "[GUID],[Int]".

Using fiddler, you can see the response which has the clue:

<?xml version="1.0" encoding="utf-8"?><m:error xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"><m:code>-1, Microsoft.SharePoint.Client.ClientServiceException</m:code><m:message xml:lang="en-US">The request ETag value '"{e9619514-4ebc-424f-8505-79a8ae02319c},1"' does not match the object's ETag value '"e9619514-4ebc-424f-8505-79a8ae02319c,1"'.</m:message></m:error>

Cause

The IF-Match header uses the eTag to maintain concurrency control. You will receive a 412 if the value of that header does not match the SharePoint value exactly.
Unfortunately, the eTag given by the SharePoint REST API when you initially create a file is not in the same format that is used when you update.

Solution

Remove the braces and lower case the eTag.

etag = etag.replace(/{|}/g, "").toLowerCase();

Alternate solution

If you want to turn off concurrency altogether (not ideal), you can use "*" as the IF-MATCH header value.

Source

For reference, here is my code to update the files list item fields using REST.
TypeScript:

public updateListItem(siteCollectionUrl:string, serverRelativeUrl: string, properties: any, etag:string = "*") {
            if (etag) {
                etag = etag.replace(/{|}/g, "").toLowerCase();
            }
            
            if (typeof (properties.__metadata) == typeof (undefined)) {
                properties.__metadata = { 'type': 'SP.ListItem' };
            }
            var url = `${siteCollectionUrl}/_api/Web/GetFileByServerRelativeUrl('${serverRelativeUrl}')/ListItemAllFields `;
            return $.ajax({
                url: url,
                type: "POST",
                data: JSON.stringify(properties),
                headers: {
                    "X-RequestDigest": $("#__REQUESTDIGEST").val(),
                    "content-type": "application/json;odata=verbose",
                    "IF-MATCH": etag,
                    "X-HTTP-Method": "MERGE"
                }
            });
        }