toeverything/AFFiNE · error · CopilotTranscriptionJobNotFound

copilot_transcription_job_not_found

copilot_transcription_job_not_found

Error message

Transcription job not found.

What it means

Thrown by CopilotTranscriptService.settleTask when models.copilotTranscriptTask.getWithUser(userId, workspaceId, taskId) returns null: no transcription task exists with that taskId, or the task exists but is scoped to a different user/workspace. It is a UserFriendlyError with HTTP class bad_request and code copilot_transcription_job_not_found.

Solutions

  1. List the user's transcript jobs for the workspace first and settle an id returned by that list
  2. Verify both userId and workspaceId match the task's owner - getWithUser filters by all three keys
  3. If the task was recreated by retryTask, use the fresh taskId/job object it returns instead of the stale one
  4. If you operate the database, confirm the row exists in the copilot_transcript_task table

Example fix

// before
const job = await client.settleTranscriptTask({ workspaceId, taskId: cachedTaskId });

// after
const jobs = await client.listTranscriptJobs(workspaceId);
const task = jobs.find(j => j.id === cachedTaskId);
if (!task) throw new Error('stale task id - refresh job list');
const job = await client.settleTranscriptTask({ workspaceId, taskId: task.id });
Defensive patterns

Strategy: validation

Validate before calling

const jobs = await client.listTranscriptJobs(workspaceId);
const task = jobs.find(j => j.id === taskId && j.workspaceId === workspaceId);
if (!task) {
  // refresh cached ids; do not call settle
  throw new Error(`transcript task ${taskId} not found for this user/workspace`);
}
await client.settleTranscriptTask({ workspaceId, taskId });

Type guard

const isTranscriptJob = (j: unknown): j is TranscriptJob =>
  typeof j === 'object' && j !== null &&
  typeof (j as TranscriptJob).id === 'string' &&
  typeof (j as TranscriptJob).status === 'string';

Try / catch

try {
  await settle(taskId);
} catch (e) {
  if (e instanceof GraphQLAuthProviderError && e.code === 'copilot_transcription_job_not_found') {
    cachedTaskIds.delete(taskId); // drop stale reference
    await refreshJobs();
  } else throw e;
}

Prevention

When it happens

Trigger: Calling the transcript settle API/mutation with a taskId that was never created, was deleted, or belongs to another user or workspace; using a taskId cached from a previous session or from a different workspace in multi-workspace clients.

Common situations: Client caches a taskId across sessions after the task row was cleaned up; wrong workspaceId passed alongside a valid taskId; task was replaced by a new upload/retry and the caller still references the old id.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


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

Appendix: source

Thrown at packages/backend/server/src/plugins/copilot/transcript/service.ts:477

    await this.retry.enqueuePendingTask(task.id, payload, generation, null);
    this.publishTaskChanged(workspaceId, task.id, AiJobStatus.pending);

    return { id: task.id, status: AiJobStatus.pending, infos };
  }

  async retryTask(userId: string, workspaceId: string, taskId: string) {
    return await this.retry.retryTask(userId, workspaceId, taskId);
  }

  async settleTask(userId: string, workspaceId: string, taskId: string) {
    const task = await this.models.copilotTranscriptTask.getWithUser(
      userId,
      workspaceId,
      taskId
    );
    if (!task) {
      throw new CopilotTranscriptionJobNotFound();
    }
    if (task.status === 'failed') {
      throw new BadRequestException(
        'Failed transcript tasks cannot be settled'
      );
    }
    if (task.status !== 'ready' && task.status !== 'settled') {
      return null;
    }

    if (task.status === 'settled') {
      return taskToJob(task);
    }

    const settled = await this.models.copilotTranscriptTask.settle(task.id);
    return taskToJob(settled);
  }

View on GitHub (pinned to 591f874dad)