toeverything/AFFiNE · warning · DocUpdateBlocked

doc_update_blocked

doc_update_blocked

Error message

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

What it means

Thrown by WorkspaceSyncAdapter.push() when the doc's metadata row has blocked === true. The block flag is read from the doc meta model before any update is written, so a blocked doc rejects all incoming Yjs updates for that (spaceId, docId). The check is specific to the Workspace space type; Userspace pushes bypass it.

Source

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

    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 26c515e050)

Solutions

  1. Check whether the doc is intentionally blocked via admin tooling before escalating.
  2. On the client, surface a read-only / locked indicator and stop pushing updates for that docId when this error is received.
  3. If the block is stale, clear doc_meta.blocked for the (spaceId, docId) in the database.
  4. Have the client re-fetch doc metadata on reconnect to learn the blocked state proactively.

Example fix

// before — keep retrying pushes on failure
socket.emit('space:push-doc-update', payload);

// after — honor the blocked state
try {
  await socket.emitWithAck('space:push-doc-update', payload);
} catch (e) {
  if (e.code === 'doc_update_blocked') {
    setDocReadOnly(spaceId, docId, true);
    return;
  }
  throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Optionally pre-fetch doc meta to learn blocked state
const meta = await fetchDocMeta(spaceId, docId);
if (meta?.blocked) { setReadOnly(docId); }

Type guard

function isDocBlocked(meta?: { blocked?: boolean }): boolean {
  return Boolean(meta?.blocked);
}

Try / catch

try {
  await socket.emitWithAck('space:push-doc-update', payload);
} catch (e) {
  if (e?.code === 'doc_update_blocked') { setDocReadOnly(spaceId, docId, true); return; }
  throw e;
}

Prevention

When it happens

Trigger: Client emits 'space:push-doc-update' against a workspace doc whose doc_meta.blocked column is true, while still being joined and permissioned. Triggered when an admin/migration has flagged the doc read-only.

Common situations: Doc was blocked during a migration, a quota enforcement action, a legal/admin hold, or because it was merged into another doc; client still has the doc open and keeps sending edits.

Related errors


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