lobehub/lobehub · error · TRPCError

NOT_FOUND

NOT_FOUND

Error message

File "${input.fileId}" not found.

What it means

Thrown by resolveAudio (called from transcribe) when input.fileId is set but FileModel.findById returns null. The FileModel is constructed userId-scoped, so findById only finds files owned by the caller — a file belonging to another user is indistinguishable from a non-existent one. This fires before any S3 access is attempted.

Source

Thrown at apps/server/src/routers/lambda/asr.ts:157

    }),
});

/**
 * Turn the request into raw audio bytes + metadata, from either a stored file
 * (downloaded from S3, ownership enforced by the userId-scoped FileModel) or the
 * inline base64 payload.
 */
async function resolveAudio(
  ctx: { serverDB: LobeChatDatabase; userId: string },
  input: { audioBase64?: string; fileId?: string; fileName?: string; mimeType?: string },
  workspaceId?: string,
): Promise<ResolvedAudio> {
  if (input.fileId) {
    const fileModel = new FileModel(ctx.serverDB, ctx.userId, workspaceId);
    const fileItem = await fileModel.findById(input.fileId);

    if (!fileItem) {
      throw new TRPCError({ code: 'NOT_FOUND', message: `File "${input.fileId}" not found.` });
    }

    const fileService = new FileService(ctx.serverDB, ctx.userId, workspaceId);
    let bytes: Uint8Array;
    try {
      bytes = await fileService.getFileByteArray(fileItem.url);
    } catch (error) {
      if ((error as { Code?: string }).Code === 'NoSuchKey') {
        throw new TRPCError({
          code: 'NOT_FOUND',
          message: `File "${input.fileId}" is no longer available in storage.`,
        });
      }
      throw error;
    }

    return { bytes, fileName: fileItem.name, mimeType: fileItem.fileType };
  }

View on GitHub (pinned to 10f24d7ade)

Solutions

  1. Re-upload the audio file via the file upload endpoint and use the freshly returned fileId.
  2. Verify the fileId was created under the same userId (and workspaceId, if applicable) as the transcribe call.
  3. Check the file still exists via the file API before passing it to transcribe.
  4. Ensure the audio was uploaded through the same environment (dev vs prod) as the transcription request.

Example fix

// before: stale or foreign fileId
await asr.transcribe({ fileId: 'old-or-wrong-id', model: 'whisper-1' }); // -> NOT_FOUND

// after: re-upload and use fresh id
const { id } = await uploadAudioFile(file);
await asr.transcribe({ fileId: id, model: 'whisper-1' });
Defensive patterns

Strategy: validation

Validate before calling

// Verify the file exists and is owned by the caller before transcribing
const file = await fileRouter.getFile.query({ id: input.fileId });
if (!file) {
  throw new Error('File not found or not owned by caller; re-upload');
}
await asrRouter.transcribe.mutate(input);

Type guard

const isExistingFile = (
  file: { id: string; userId: string } | null | undefined,
  userId: string
): file is { id: string; userId: string } =>
  !!file && file.userId === userId;

Try / catch

try {
  await asrRouter.transcribe.mutate(input);
} catch (e) {
  if (e.shape?.data?.code === 'NOT_FOUND' && /File.*not found/.test(e.message)) {
    // prompt re-upload
  } else throw e;
}

Prevention

When it happens

Trigger: Calling transcribe with a fileId that does not exist in the files table, or exists but belongs to a different userId/workspace. The query at FileModel.findById(input.fileId) returns null because of the ownership scoping.

Common situations: Stale fileId from a deleted file. Cross-user/cross-workspace fileId leak. A typo or truncated id. File uploaded to a different workspace than the one the transcription call is scoped to.

Related errors


AI-assisted analysis of lobehub/lobehub@10f24d7ade (2026-08-12). Data as JSON: /api/errors/ca4bfcf25b0d55f9. Report an issue: GitHub.