toeverything/AFFiNE · error · DocUpdateBlocked

doc_update_blocked

doc_update_blocked

Error message

Doc ${docId} under Space ${spaceId} is blocked from updating.

What it means

WorkspaceSyncAdapter.push loads the doc's metadata before persisting updates and throws doc_update_blocked when the blocked flag is true. A blocked doc is server-side write-locked (for example by moderation or a restore/maintenance flow): reads such as diff keep working, but new update pushes are rejected.

Solutions

  1. Stop retrying pushes for that doc immediately - the block is an intentional server state, not a transient fault
  2. Buffer the rejected updates locally (Yjs updates merge safely) and flush them once the doc is unblocked
  3. Check the doc's blocked state through the doc metadata/API to detect when writes are allowed again
  4. If you operate the server, finish or cancel the maintenance operation that set the flag

Example fix

// before
await adapter.push(spaceId, docId, updates, editorId);

// after
try {
  await adapter.push(spaceId, docId, updates, editorId);
} catch (e) {
  if (e.code === 'doc_update_blocked') {
    pendingUpdates.push(...updates); // buffer, retry when unblocked
  } else {
    throw e;
  }
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
  await push(spaceId, docId, updates, editorId);
} catch (e) {
  if (e?.code === 'doc_update_blocked') {
    pendingUpdates.push(...updates); // buffer Yjs updates locally
    scheduleRetryWhenUnblocked(spaceId, docId); // e.g. poll doc metadata
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: Pushing doc updates via the sync gateway while models.doc.getMeta(spaceId, docId) has blocked=true; a maintenance/restore job flipping the flag while clients sync; admin tooling that blocks a doc.

Common situations: Self-hosted instance in the middle of a doc migration or backup restore; a doc flagged for review; clients misreading the rejection as a network error and retry-storming.

Related errors


AI-assisted analysis of toeverything/AFFiNE@b6de0ad51b (2026-08-18). Data as JSON: /api/errors/b69a5fa96cafa933. Report an issue: GitHub.

Appendix: source

Thrown at packages/backend/server/src/core/sync/gateway.ts:1532

    private readonly docReader: DocReader,
    private readonly models: Models
  ) {
    super(SpaceType.Workspace, client, storage);
  }

  override async push(
    spaceId: string,
    docId: string,
    updates: Buffer[],
    editorId: string
  ) {
    const docMeta = await this.models.doc.getMeta(spaceId, docId, {
      select: {
        blocked: true,
      },
    });
    if (docMeta?.blocked) {
      throw new DocUpdateBlocked({ spaceId, docId });
    }
    return await super.push(spaceId, docId, updates, editorId);
  }

  override async diff(
    spaceId: string,
    docId: string,
    stateVector?: Uint8Array
  ) {
    return await this.docReader.getDocDiff(spaceId, docId, stateVector);
  }

  async assertAccessible(
    spaceId: string,
    userId: string,
    action: WorkspaceAction
  ) {
    await this.ac.user(userId).workspace(spaceId).assert(action);

View on GitHub (pinned to b6de0ad51b)