{"record":{"id":"7e2b2c9f0e546468","repo":"ruvnet/ruflo","slug":"file-not-found-7e2b2c","errorCode":null,"errorMessage":"File not found","messagePattern":"File not found","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"ruflo/src/ruvocal/src/lib/server/database/rvf.ts","lineNumber":1056,"sourceCode":"\t\t\t\t\tfilename,\n\t\t\t\t\tcontentType: options?.contentType ?? \"application/octet-stream\",\n\t\t\t\t\tlength: data.length,\n\t\t\t\t\tdata,\n\t\t\t\t\tmetadata: options?.metadata ?? {},\n\t\t\t\t\tcreatedAt: new Date(),\n\t\t\t\t});\n\t\t\t\tscheduleSave();\n\t\t\t},\n\t\t};\n\t}\n\n\topenDownloadStream(id: ObjectId | string) {\n\t\tconst fileId = typeof id === \"string\" ? id : id.toString();\n\t\tconst files = this.files;\n\t\treturn {\n\t\t\tasync toArray(): Promise<Buffer[]> {\n\t\t\t\tconst file = files.get(fileId);\n\t\t\t\tif (!file) throw new Error(\"File not found\");\n\t\t\t\treturn [Buffer.from(file.data as string, \"base64\")];\n\t\t\t},\n\t\t};\n\t}\n\n\tasync delete(id: ObjectId | string) {\n\t\tconst fileId = typeof id === \"string\" ? id : id.toString();\n\t\tthis.files.delete(fileId);\n\t\tscheduleSave();\n\t}\n\n\tasync find(filter: Record<string, unknown> = {}) {\n\t\tconst results: Record<string, unknown>[] = [];\n\t\tfor (const doc of this.files.values()) {\n\t\t\tif (matchesFilter(doc, filter)) {\n\t\t\t\tconst { data, ...meta } = doc;\n\t\t\t\tresults.push(meta);\n\t\t\t}","sourceCodeStart":1038,"sourceCodeEnd":1074,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/ruflo/src/ruvocal/src/lib/server/database/rvf.ts#L1038-L1074","documentation":"In the rvf (file-backed/in-memory) storage backend, openDownloadStream(id).toArray() looks up the base64 blob in an in-memory files Map (rvf.ts:1056) and throws \"File not found\" on a miss. The Map is hydrated from the on-disk store (./db JSON persistence) at startup, so a miss means the id was never persisted in this store or the persisted data is gone.","triggerScenarios":"Awaiting bucket.openDownloadStream(fileId).toArray() when the rvf store's files Map has no entry for the id: server restarted with a missing/empty store file, DATA_DIR/working directory changed between runs (scheduleSave writes relative paths), conversations imported from another backend, or the file was deleted via bucket.delete().","commonSituations":"Dev server run from a different cwd so ./db resolves elsewhere; wiping the db folder but keeping conversation records; switching MONGODB_URL on/off so uploads landed in one backend and reads hit another; concurrent saves losing a just-uploaded file after a crash.","solutions":["Confirm which store the file was written to (check the db JSON file's files keys) and read from the same backend","Return a friendly 404 from the download path when the blob is gone, and clean the dangling message reference","Pin the store location with an absolute DATA_DIR so restarts from any cwd see the same data","Re-upload or restore the store file from backup"],"exampleFix":"// before\nconst [buf] = await collections.bucket.openDownloadStream(fileId).toArray(); // throws: File not found\n\n// after\nconst existing = await collections.bucket.find({ _id: fileId }).toArray();\nif (existing.length === 0) throw error(404, \"File no longer available\");\nconst [buf] = await collections.bucket.openDownloadStream(fileId).toArray();","handlingStrategy":"try-catch","validationCode":"const existing = await collections.bucket.find({ _id: fileId }).toArray();\nif (existing.length === 0) throw error(404, \"File no longer available\");","typeGuard":"null","tryCatchPattern":"try {\n\tconst [buf] = await collections.bucket.openDownloadStream(fileId).toArray();\n} catch (e) {\n\tif (e instanceof Error && e.message === \"File not found\") throw error(404, \"File expired\");\n\tthrow e;\n}","preventionTips":["Pin the rvf store to an absolute path (stable cwd/DATA_DIR) so restarts see the same files","Treat upload+persist as atomic — call scheduleSave immediately after writes","Don't mix backends across restarts (MONGODB_URL set vs unset) for the same data set"],"tags":["rvf","file-storage","in-memory","not-found","persistence"],"backgroundTag":"file-not-found","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}