Skip to main content
When a request fails, the MDCMS API returns a structured error response with enough detail to diagnose and resolve the issue programmatically.

Error Response Format

All errors follow a consistent envelope:
{
  "error": {
    "status": "error",
    "code": "SCHEMA_HASH_MISMATCH",
    "message": "The provided schema hash does not match the current server schema. Run `mdcms schema sync` to update.",
    "statusCode": 409,
    "details": {
      "expectedHash": "sha256_abc123",
      "providedHash": "sha256_def456"
    },
    "requestId": "req_7f3a2b1c4d5e6f",
    "timestamp": "2026-01-15T09:30:00.000Z"
  }
}
FieldTypeDescription
statusstringAlways "error".
codestringMachine-readable error code. Use this for programmatic error handling.
messagestringHuman-readable description of what went wrong.
statusCodeintegerHTTP status code (duplicated from the response status for convenience).
detailsobjectAdditional context specific to the error type. May be empty {}.
requestIdstringUnique identifier for this request. Include this when reporting issues.
timestampstringISO 8601 timestamp of when the error occurred.
Every error response includes a requestId. When contacting support or filing bug reports, include the requestId to help trace the request through server logs.

Error Codes

CodeHTTP StatusDescriptionCommon Cause
UNAUTHORIZED401Missing or invalid authentication credentials.Expired API key, missing Authorization header, invalid session cookie.
FORBIDDEN403Authenticated but insufficient permissions.API key lacks the required scope, or user role does not include the needed capability.
NOT_FOUND404The requested resource does not exist.Invalid document ID, deleted document, or wrong project/environment.
INVALID_INPUT400Request body failed validation.Missing required fields, wrong data types, or values that violate schema constraints.
INVALID_QUERY_PARAM400A query parameter has an invalid value.Non-numeric limit, unsupported sort field, or unknown filter parameter.
INVALID_CONTENT_SCOPE400The content scope in the request is invalid.Requesting a type that does not exist, or a locale that is not configured for the project.
SCHEMA_NOT_FOUND404The requested schema type does not exist in the registry.Querying for a type name that has not been synced.
SCHEMA_HASH_REQUIRED400Content write operation attempted without schema hash header.Missing X-MDCMS-Schema-Hash header on a create or update request.
SCHEMA_HASH_MISMATCH409The schema hash provided does not match the server’s current schema.Local schema is out of date. Run mdcms schema sync to update.
SCHEMA_NOT_SYNCED409No schema has been synced to the server yet.The project has no schema registry entries. Run mdcms schema sync first.
CONTENT_PATH_CONFLICT409A document already exists at the given path and locale.Attempting to create a document with a path that is already taken.
CONFLICT409Optimistic concurrency conflict on document update.The draftRevision in the request body is stale. Re-fetch the document, merge changes, and retry with the current revision.
RATE_LIMITED429Too many requests.Exceeding the rate limit for the API key or session. Back off and retry after the Retry-After header value.
INTERNAL_ERROR500An unexpected server error occurred.Server bug or infrastructure issue. Report with the requestId.

Handling Specific Errors

Schema hash mismatch (409):
try {
  await createDocument(payload);
} catch (error) {
  if (error.code === "SCHEMA_HASH_MISMATCH") {
    // Re-sync schema and retry
    await syncSchema();
    await createDocument(payload);
  }
}
Optimistic concurrency conflict (409):
try {
  await updateDocument(documentId, {
    frontmatter: updatedData,
    draftRevision: localRevision,
  });
} catch (error) {
  if (error.code === "CONFLICT") {
    // Fetch latest version and merge
    const latest = await getDocument(documentId);
    const merged = mergeChanges(localChanges, latest);
    await updateDocument(documentId, {
      frontmatter: merged,
      draftRevision: latest.draftRevision,
    });
  }
}
Rate limiting (429):
try {
  await listDocuments(params);
} catch (error) {
  if (error.code === "RATE_LIMITED") {
    const retryAfter = parseInt(error.details.retryAfter, 10);
    await sleep(retryAfter * 1000);
    await listDocuments(params);
  }
}

Resolve Errors

When you use the resolve parameter to expand references in a content response, some references may fail to resolve. Instead of failing the entire request, the API returns the document with null values for unresolvable references and includes error details in the resolveErrors field.

Resolve Error Format

{
  "data": {
    "documentId": "550e8400-e29b-41d4-a716-446655440000",
    "type": "BlogPost",
    "frontmatter": {
      "title": "Hello World",
      "author": null,
      "relatedPosts": [
        {
          "documentId": "...",
          "type": "BlogPost",
          "frontmatter": { "title": "Other Post" }
        },
        null
      ]
    },
    "resolveErrors": {
      "frontmatter.author": {
        "code": "REFERENCE_DELETED",
        "referenceId": "660e8400-e29b-41d4-a716-446655440001",
        "message": "The referenced Author document has been deleted."
      },
      "frontmatter.relatedPosts[1]": {
        "code": "REFERENCE_NOT_FOUND",
        "referenceId": "770e8400-e29b-41d4-a716-446655440002",
        "message": "The referenced BlogPost document was not found."
      }
    }
  }
}

Resolve Error Codes

CodeDescription
REFERENCE_NOT_FOUNDThe referenced document does not exist in the target project and environment. It may have been deleted permanently or never existed.
REFERENCE_DELETEDThe referenced document exists but has been soft-deleted. It can be restored via the restore endpoint.
REFERENCE_TYPE_MISMATCHThe referenced document exists but its type does not match the expected target type declared in the schema. This typically indicates a data integrity issue.
REFERENCE_FORBIDDENThe current API key or user does not have content:read permission for the referenced document. The reference exists but cannot be resolved with the current credentials.
Resolve errors are informational and do not cause the request to fail. Always check the resolveErrors field when using resolved references, especially in production applications where referenced content may change independently.