toeverything/AFFiNE · warning · CopilotDocNotFound
copilot_doc_not_found
copilot_doc_not_found
Error message
Doc ${docId} not found. What it means
forkCopilotSession rejects the request when options.docId === options.workspaceId: in AFFiNE the workspace root doc shares the workspace's id, and chat sessions cannot be forked onto the root doc, so the guard throws CopilotDocNotFound (code `copilot_doc_not_found`, status `resource_not_found`) with the offending docId. It is an input-shape rejection, not a missing document.
Source
Thrown at packages/backend/server/src/plugins/copilot/resolver.ts:717
@Mutation(() => String, {
description: 'Create a chat session',
})
@CallMetric('ai', 'chat_session_fork')
async forkCopilotSession(
@CurrentUser() user: CurrentUser,
@Args({ name: 'options', type: () => ForkChatSessionInput })
options: ForkChatSessionInput
): Promise<string> {
await this.ac.user(user.id).doc(options).allowLocal().assert('Doc.Update');
const lockFlag = `${COPILOT_LOCKER}:session:${user.id}:${options.workspaceId}`;
await using lock = await this.mutex.acquire(lockFlag);
if (!lock) {
throw new TooManyRequest('Server is busy');
}
if (options.workspaceId === options.docId) {
// filter out session create request for root doc
throw new CopilotDocNotFound({ docId: options.docId });
}
return await this.chatSession.fork({
...options,
userId: user.id,
});
}
@Mutation(() => [String], {
description: 'Cleanup sessions',
})
@CallMetric('ai', 'chat_session_cleanup')
async cleanupCopilotSession(
@CurrentUser() user: CurrentUser,
@Args({ name: 'options', type: () => DeleteSessionInput })
options: DeleteSessionInput
): Promise<string[]> {
const { workspaceId, docId, sessionIds } = options;View on GitHub (pinned to b4c8548c09)
Solutions
- Pass the real child doc id as docId, or omit docId when forking at workspace level (check ForkChatSessionInput optionality)
- Guard before the call: if (options.docId === options.workspaceId) fix the payload instead of sending it
- Log the fork payload at the call site to confirm which field carries the workspace id
Example fix
// before
forkCopilotSession({
variables: { options: { sessionId, workspaceId, docId: workspaceId } }, // root doc id
});
// after
forkCopilotSession({
variables: { options: { sessionId, workspaceId, docId: realDocId } }, // child doc id
}); Defensive patterns
Strategy: validation
Validate before calling
// guard: reject the root-doc misuse before sending
function isForkInputValid(o: ForkChatSessionInput): boolean {
return o.docId == null || (o.docId !== o.workspaceId && /^[0-9a-f-]{36}$/i.test(o.docId));
}
if (!isForkInputValid(options)) throw new Error('docId must be a child doc, not the workspace root'); Type guard
function isCopilotDocNotFound(e: unknown): boolean {
return (e as { extensions?: { code?: string } })?.extensions?.code === 'copilot_doc_not_found';
} Try / catch
try {
await forkCopilotSession({ variables: { options } });
} catch (e) {
if (isCopilotDocNotFound(e)) {
// payload sent the workspace id as docId; rebuild options with the real doc id
showPickDocDialog();
} else throw e;
} Prevention
- Type fork options explicitly and never default docId to workspace.id
- When forking from workspace context, require an explicit doc selection
- Unit-test the fork payload builder for the workspace-root case
When it happens
Trigger: Calling forkCopilotSession with the workspace id passed as docId — typically a client that defaults docId to workspace.id, spreads the wrong object into ForkChatSessionInput, or stores the root doc id where a child doc id is expected.
Common situations: Forking from a workspace-level context where no doc is open and the client fills docId with workspace.id; refactors that confuse `workspaceId`/`docId` in fork options; copied code from a doc-level fork used on the workspace root.
Related errors
- email_token_not_found
- same_email_provided
- copilot_selected_sources_limit_exceeded
- doc_not_found
- query_too_long
AI-assisted analysis of toeverything/AFFiNE@b4c8548c09 (2026-08-18).
Data as JSON: /api/errors/0ad6ec844a94581d.
Report an issue: GitHub.