toeverything/AFFiNE · error · CopilotMessageNotFound
copilot_message_not_found
copilot_message_not_found
Error message
Copilot message ${messageId} not found. What it means
loadAcceptedTurn found an accepted submission record for messageId, but its sessionId does not match the session the request is operating on, so it throws CopilotMessageNotFound (code `copilot_message_not_found`, status `resource_not_found`) with that messageId. The message exists — just in a different chat session.
Source
Thrown at packages/backend/server/src/plugins/copilot/runtime/hosts/conversation-host.ts:166
) {
throw new CopilotSelectedSourcesLimitExceeded();
}
throw error;
}
const scopeSnapshot = TurnScopeSnapshotSchema.parse(compiledScope);
return { artifacts, focus, metadata, scopeSnapshot };
}
private async loadAcceptedTurn(
session: ChatSession,
sessionId: string,
messageId: string,
retry: boolean
): Promise<Turn | undefined> {
const accepted = await this.submissions.getAccepted(messageId);
if (!accepted) return;
if (accepted.sessionId !== sessionId) {
throw new CopilotMessageNotFound({ messageId });
}
if (retry) {
await this.sessions.revertLatestMessage(sessionId, false);
session.revertLatestMessage(false);
}
const existingTurn = session.findTurn(accepted.turnId);
if (existingTurn) return existingTurn;
const acceptedMessage = await this.sessions.getMessage(
sessionId,
accepted.turnId
);
if (acceptedMessage.role !== 'user') {
throw new CopilotMessageNotFound({ messageId: accepted.turnId });
}
View on GitHub (pinned to b4c8548c09)
Solutions
- Always take messageId and sessionId from the same session payload (e.g. the history query result)
- After forking, refresh history and use the new session's message ids
- On this 404, refetch session history and retry with the correct id pair
Defensive patterns
Strategy: validation
Validate before calling
// guard: only use message ids that belong to this session
const history = await fetchHistory(sessionId);
const valid = history.messages.some(m => m.id === messageId);
if (!valid) throw new Error('messageId does not belong to this session');
await prepareTurn(sessionId, messageId); Type guard
function isMessageNotFound(e: unknown): boolean {
return (e as { extensions?: { code?: string } })?.extensions?.code === 'copilot_message_not_found';
} Try / catch
try {
await prepareTurn(sessionId, messageId);
} catch (e) {
if (isMessageNotFound(e)) {
const fresh = await fetchHistory(sessionId); // re-pair ids from one payload
const lastUser = fresh.messages.findLast(m => m.role === 'user');
if (lastUser) return prepareTurn(sessionId, lastUser.id);
}
throw e;
} Prevention
- Always source sessionId and messageId from the same history payload
- After a fork, refetch the new session's history before continuing
- Never cache message ids across sessions
When it happens
Trigger: Retrying or continuing a conversation using a messageId that belongs to another session: ids mixed up after a fork (old session's message id sent to the forked session), or a client reusing cached ids across sessions.
Common situations: After forkCopilotSession, continuing with the pre-fork message ids; state-management bug pairing a messageId from session A with sessionId B; deep links carrying stale messageId query params.
Related errors
- copilot_session_not_found
- blob_not_found
- Workspace not found
- Session not found
- copilot_session_not_found
AI-assisted analysis of toeverything/AFFiNE@b4c8548c09 (2026-08-18).
Data as JSON: /api/errors/66a94167ac300212.
Report an issue: GitHub.