{"record":{"id":"f578c0b7dc51ca5e","repo":"Mintplex-Labs/anything-llm","slug":"file-not-found-in-storage","errorCode":null,"errorMessage":"File not found in storage","messagePattern":"File not found in storage","errorType":"http","errorClass":null,"httpStatus":404,"severity":"warning","filePath":"server/endpoints/agentFileServer.js","lineNumber":63,"sourceCode":"        }\n\n        // Find a chat or scheduled job run that references this file\n        const fileSource = await findFileSource(filename, {\n          user,\n          isMultiUser: multiUserMode(response),\n        });\n\n        if (!fileSource) {\n          return response.status(404).json({\n            error: \"File not found or access denied\",\n          });\n        }\n\n        // Retrieve the file from storage\n        const fileData = await createFilesLib.getGeneratedFile(filename);\n        if (!fileData) {\n          return response\n            .status(404)\n            .json({ error: \"File not found in storage\" });\n        }\n\n        // Get mime type and set headers for download\n        const mimeType = createFilesLib.getMimeType(`.${parsed.extension}`);\n        const safeFilename = createFilesLib.sanitizeFilenameForHeader(\n          fileSource.displayFilename || filename\n        );\n        response.setHeader(\"Content-Type\", mimeType);\n        response.setHeader(\n          \"Content-Disposition\",\n          `attachment; filename=\"${safeFilename}\"`\n        );\n        response.setHeader(\"Content-Length\", fileData.buffer.length);\n        response.send(fileData.buffer);\n        Telemetry.sendTelemetry(\"agent_generated_file_downloaded\", {\n          type: mimeType,\n        }).catch(() => {});","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/Mintplex-Labs/anything-llm/blob/3aec848f2885144aa8f1e53b9731a04310d5d558/server/endpoints/agentFileServer.js#L45-L81","documentation":"Returned (HTTP 404) by GET /agent-skills/generated-files/:filename when findFileSource DID find an authorizing chat/job record, but createFilesLib.getGeneratedFile(filename) returned null — the database references the file while the storage layer does not have the blob. This is DB/storage desync: authorization succeeded (unlike the 'access denied' 404), the file on disk or in the storage driver is simply gone.","triggerScenarios":"The storage directory was pruned or deleted while chat history rows remained; STORAGE_DIR/storage location env differs between the generating and serving instances (common with multiple replicas or after a migration); the file was manually removed from storage; a docker volume was not mounted into the container serving downloads.","commonSituations":"Wiping storage folders to 'clean up' without clearing chat history; moving deployments to new hosts; replica with a different mounted volume serving a link created by another replica; backup restored for the DB but not the storage volume.","solutions":["Check the server's generated-files storage directory actually contains the filename","Align the storage path configuration across all instances serving these URLs","Regenerate the file by re-running the chat request that produced it","If storage was intentionally pruned, clean the referencing chat/job rows too so links fail fast with the access-denied 404 instead"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Operator-side preflight: the DB row and the stored blob must both exist\nconst rows = await db.query('select 1 from workspace_chats where history like ?', [`%${filename}%`]);\nconst onDisk = fs.existsSync(path.join(storageDir, 'generated-files', filename));\nif (rows.length && !onDisk) console.warn('orphaned reference:', filename);","typeGuard":null,"tryCatchPattern":"const res = await fetch(url, {credentials: 'include'});\nif (res.status === 404) {\n  const body = await res.json();\n  if (body.error === 'File not found in storage')\n    regenerateArtifact(); // authorization passed; the blob is gone — only regeneration helps\n}","preventionTips":["Back up storage and the database together; never wipe one without the other","Pin the storage path configuration identically across replicas and environments","After pruning storage, delete referencing chat/job records in the same change"],"tags":["http-404","storage","db-storage-desync","file-download"],"backgroundTag":"file-missing-from-storage","analyzedSha":"3aec848f2885144aa8f1e53b9731a04310d5d558","analyzedAt":"2026-08-18T10:02:21.017Z","contentChangedAt":"2026-08-18T10:02:21.017Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}