Skip to main content
423

Locked

Active
RFC 4918 §11.3Since 2007WebDAV, CalDAV, CardDAV, SharePoint, Nextcloud, collaborative editing APIs

HTTP 423 Locked indicates the source or destination resource is currently locked. Defined in RFC 4918 §11.3 (WebDAV). The resource cannot be modified until the lock holder releases it via UNLOCK with the lock token. Common in distributed authoring, collaborative editing, and pessimistic locking APIs.

Description

The 423 Locked status code means the source or destination resource of a method is locked. This response SHOULD contain an appropriate precondition or postcondition code, such as 'lock-token-submitted' or 'no-conflicting-lock', to help the client resolve the situation.

423 originated in WebDAV (Web Distributed Authoring and Versioning), designed for collaborative document editing over HTTP. WebDAV defines two lock types: exclusive locks (only the lock owner can modify the resource) and shared locks (multiple principals can hold concurrent read access, but writes are still serialized). A lock is acquired via the LOCK method and released via UNLOCK, with the lock token passed in the Lock-Token header.

The distinction from related status codes is precise. 409 Conflict means the resource's state contradicts the request (version mismatch, duplicate key). 412 Precondition Failed means a conditional header (If-Match, If-Unmodified-Since) evaluated to false. 423 Locked means a specific lock mechanism is preventing access — the resource isn't in a bad state, it's just reserved by another principal. The request would succeed if the lock were released.

Outside WebDAV, some REST APIs adopt 423 for pessimistic locking scenarios: a document being edited by another user in a collaborative tool, a database row under an advisory lock during a long-running transaction, or an entity being processed by a background job that requires exclusive access. CalDAV, CardDAV, SharePoint, Nextcloud, and similar platforms use 423 natively as part of their WebDAV heritage.

The resolution path is clear: wait for the lock to expire (locks have timeouts), request the lock holder to release, or — if you are the lock holder and lost the token — use lock recovery mechanisms. Some servers include the lock owner, lock scope (exclusive/shared), lock type, and timeout in the 423 response body as a DAV:multistatus XML structure.

Examples

Attempting to PUT a locked WebDAV resource without lock token
http
PUT /webdav/reports/Q4-financials.docx HTTP/1.1
Host: dav.example.com
Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document
Authorization: Bearer user-alice-token

<binary document content>
423 response — resource locked by another user
http
HTTP/1.1 423 Locked
Content-Type: application/xml; charset=utf-8

<?xml version="1.0" encoding="utf-8"?>
<D:error xmlns:D="DAV:">
  <D:lock-token-submitted>
    <D:href>/webdav/reports/Q4-financials.docx</D:href>
  </D:lock-token-submitted>
</D:error>
LOCK request — acquiring an exclusive write lock
http
LOCK /webdav/reports/Q4-financials.docx HTTP/1.1
Host: dav.example.com
Authorization: Bearer user-bob-token
Content-Type: application/xml; charset=utf-8
Timeout: Second-3600

<?xml version="1.0" encoding="utf-8"?>
<D:lockinfo xmlns:D="DAV:">
  <D:lockscope><D:exclusive/></D:lockscope>
  <D:locktype><D:write/></D:locktype>
  <D:owner>
    <D:href>mailto:[email protected]</D:href>
  </D:owner>
</D:lockinfo>
LOCK response — lock token granted
http
HTTP/1.1 200 OK
Lock-Token: <urn:uuid:e71d4fae-5dec-22d6-fea5-00a0c91e6be4>
Content-Type: application/xml; charset=utf-8

<?xml version="1.0" encoding="utf-8"?>
<D:prop xmlns:D="DAV:">
  <D:lockdiscovery>
    <D:activelock>
      <D:lockscope><D:exclusive/></D:lockscope>
      <D:locktype><D:write/></D:locktype>
      <D:depth>infinity</D:depth>
      <D:owner><D:href>mailto:[email protected]</D:href></D:owner>
      <D:timeout>Second-3600</D:timeout>
      <D:locktoken>
        <D:href>urn:uuid:e71d4fae-5dec-22d6-fea5-00a0c91e6be4</D:href>
      </D:locktoken>
    </D:activelock>
  </D:lockdiscovery>
</D:prop>
REST API — 423 for entity under background processing
http
PATCH /api/invoices/inv_8xKq2mN HTTP/1.1
Host: api.example.com
Content-Type: application/json
Authorization: Bearer api-key-123 
{"status": "void"}
423 response — REST API pessimistic lock
http
HTTP/1.1 423 Locked
Content-Type: application/json
Retry-After: 30

{"error": "resource_locked", "message": "Invoice inv_8xKq2mN is currently being processed by a payment reconciliation job. Try again after the job completes.", "locked_by": "job/reconcile_batch_20240115", "lock_acquired_at": "2024-01-15T10:45:00Z", "lock_expires_at": "2024-01-15T10:50:00Z", "retry_after_seconds": 30}
UNLOCK request — releasing the lock
http
UNLOCK /webdav/reports/Q4-financials.docx HTTP/1.1
Host: dav.example.com
Authorization: Bearer user-bob-token
Lock-Token: <urn:uuid:e71d4fae-5dec-22d6-fea5-00a0c91e6be4>

Edge Cases

  • 423 requires a lock MECHANISM — if the server just doesn't want to process the request due to general state conflict without a formal lock, use 409 Conflict instead.
  • Lock tokens MUST be submitted in the If header (not Lock-Token) when modifying a locked resource you own: `If: (<urn:uuid:token>)`. The Lock-Token header is only for UNLOCK requests.
  • Depth:infinity locks on collections lock ALL descendants. A 423 on a child resource may be caused by a lock on a parent collection — include the lock root in the response.
  • Lock timeouts are advisory, not guaranteed. Servers MAY revoke locks before timeout expiry due to administrative action or resource pressure. Clients must handle unexpected lock loss.
  • Shared locks allow multiple lock holders for read access, but a write attempt by any holder still returns 423 if another shared-lock holder hasn't released. Shared does NOT mean concurrent writes.
  • Some REST APIs return 423 with a Retry-After header to indicate when the lock will expire — this is non-standard (RFC 4918 doesn't define Retry-After for 423) but pragmatically useful.
  • Lock-null resources: WebDAV allows locking a URL that doesn't exist yet (to reserve it for creation). A 423 can occur on a resource that appears to not exist from a GET perspective.

When You'll See This

  • Another user has an exclusive write lock on a shared document in SharePoint or Nextcloud
  • CalDAV server rejects a calendar event update because another client holds the lock
  • Background job holds a pessimistic lock on a database entity during batch processing
  • Attempting to delete a file in a WebDAV collection that has a depth:infinity lock from a parent
  • Collaborative editing tool prevents concurrent modifications to the same section
  • CI/CD pipeline locks a deployment resource during an active rollout
  • API resource is temporarily locked during a multi-step saga/transaction that requires exclusive access

Implementation References

LanguageConstant
Gohttp.StatusLocked
Rusthttp::StatusCode::LOCKED
Pythonhttp.HTTPStatus.LOCKED
Node.jshttp.STATUS_CODES[423]
.NETHttpStatusCode.Locked
JavaHttpStatus.LOCKED (Spring)
PHPResponse::HTTP_LOCKED (Symfony)
Ruby:locked (Rack)

History

Introduced in RFC 2518 (1999) as part of the original WebDAV specification. Updated and refined in RFC 4918 (2007), which clarified lock semantics, lock-null resources, and error reporting. WebDAV itself was motivated by the need for collaborative authoring over HTTP — a group at UC Irvine and Xerox PARC wanted to make the web a writable medium, not just readable. The 423 status code filled a gap that HTTP's conditional headers (If-Match, If-Unmodified-Since) couldn't address: explicit, time-bounded, identity-aware locking with tokens. While WebDAV never achieved universal adoption for general web authoring, it became the backbone of enterprise file systems (SharePoint, Nextcloud), calendar/contact sync (CalDAV, CardDAV), and collaborative editing platforms. The 423 code has been adopted outside WebDAV by APIs that implement pessimistic locking semantics, though it remains registered in the IANA HTTP Status Code Registry under WebDAV.

Related Status Codes

Related Headers

FAQ

When should I use 423 Locked vs 409 Conflict?

Use 423 when a formal lock mechanism is in place — the resource is explicitly locked by another principal with a lock token, timeout, and release mechanism. Use 409 when there's a state conflict without a lock: duplicate unique field, version mismatch (ETag), or invalid state transition. The key distinction: 423 implies a specific lock that will eventually be released (or can be forcibly broken), while 409 implies the client needs to change something about its request to resolve the conflict.

How do WebDAV lock tokens work with the If header?

When a client acquires a lock via the LOCK method, the server returns a lock token (typically a URN UUID) in the Lock-Token response header. For subsequent modifications to the locked resource, the client submits this token in the If request header: `If: (<urn:uuid:token-value>)`. This proves lock ownership. Without the correct token, the server returns 423. The Lock-Token header is only used in UNLOCK requests to release the lock. This two-header design separates lock proof (If) from lock release (Lock-Token).

Is 423 appropriate for REST APIs that don't use WebDAV?

Yes, but use it specifically for pessimistic locking scenarios — not as a general 'resource busy' signal. If your API has explicit lock/unlock semantics (lock acquisition, lock tokens or IDs, lock expiry, lock owner tracking), 423 is semantically correct. If you just want to signal that the resource is temporarily unavailable without formal lock mechanics, consider 409 Conflict (state-based) or 503 Service Unavailable (infrastructure-based). Many modern APIs (Figma, Notion, Google Docs) use 423 for collaborative editing locks with good results.