{"record":{"id":"ca4bfcf25b0d55f9","repo":"lobehub/lobehub","slug":"not-found-ca4bfc","errorCode":"NOT_FOUND","errorMessage":"File \"${input.fileId}\" not found.","messagePattern":"File \"(.+?)\" not found\\.","errorType":"exception","errorClass":"TRPCError","httpStatus":404,"severity":"error","filePath":"apps/server/src/routers/lambda/asr.ts","lineNumber":157,"sourceCode":"    }),\n});\n\n/**\n * Turn the request into raw audio bytes + metadata, from either a stored file\n * (downloaded from S3, ownership enforced by the userId-scoped FileModel) or the\n * inline base64 payload.\n */\nasync function resolveAudio(\n  ctx: { serverDB: LobeChatDatabase; userId: string },\n  input: { audioBase64?: string; fileId?: string; fileName?: string; mimeType?: string },\n  workspaceId?: string,\n): Promise<ResolvedAudio> {\n  if (input.fileId) {\n    const fileModel = new FileModel(ctx.serverDB, ctx.userId, workspaceId);\n    const fileItem = await fileModel.findById(input.fileId);\n\n    if (!fileItem) {\n      throw new TRPCError({ code: 'NOT_FOUND', message: `File \"${input.fileId}\" not found.` });\n    }\n\n    const fileService = new FileService(ctx.serverDB, ctx.userId, workspaceId);\n    let bytes: Uint8Array;\n    try {\n      bytes = await fileService.getFileByteArray(fileItem.url);\n    } catch (error) {\n      if ((error as { Code?: string }).Code === 'NoSuchKey') {\n        throw new TRPCError({\n          code: 'NOT_FOUND',\n          message: `File \"${input.fileId}\" is no longer available in storage.`,\n        });\n      }\n      throw error;\n    }\n\n    return { bytes, fileName: fileItem.name, mimeType: fileItem.fileType };\n  }","sourceCodeStart":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/lobehub/lobehub/blob/10f24d7ade75139093a9373b364f6bc91f3cd7db/apps/server/src/routers/lambda/asr.ts#L139-L175","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Re-upload the audio file via the file upload endpoint and use the freshly returned fileId.","Verify the fileId was created under the same userId (and workspaceId, if applicable) as the transcribe call.","Check the file still exists via the file API before passing it to transcribe.","Ensure the audio was uploaded through the same environment (dev vs prod) as the transcription request."],"exampleFix":"// before: stale or foreign fileId\nawait asr.transcribe({ fileId: 'old-or-wrong-id', model: 'whisper-1' }); // -> NOT_FOUND\n\n// after: re-upload and use fresh id\nconst { id } = await uploadAudioFile(file);\nawait asr.transcribe({ fileId: id, model: 'whisper-1' });","handlingStrategy":"validation","validationCode":"// Verify the file exists and is owned by the caller before transcribing\nconst file = await fileRouter.getFile.query({ id: input.fileId });\nif (!file) {\n  throw new Error('File not found or not owned by caller; re-upload');\n}\nawait asrRouter.transcribe.mutate(input);","typeGuard":"const isExistingFile = (\n  file: { id: string; userId: string } | null | undefined,\n  userId: string\n): file is { id: string; userId: string } =>\n  !!file && file.userId === userId;","tryCatchPattern":"try {\n  await asrRouter.transcribe.mutate(input);\n} catch (e) {\n  if (e.shape?.data?.code === 'NOT_FOUND' && /File.*not found/.test(e.message)) {\n    // prompt re-upload\n  } else throw e;\n}","preventionTips":["Always upload the audio via the file API immediately before transcribing; avoid caching fileIds.","Ensure the file upload and transcription use the same userId/workspaceId.","Filter file pickers to files owned by the current user to avoid cross-user id leakage."],"tags":["asr","file","not-found","ownership"],"backgroundTag":null,"analyzedSha":"10f24d7ade75139093a9373b364f6bc91f3cd7db","analyzedAt":"2026-08-12T11:43:19.543Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}