ruvnet/ruflo · error · Error
File not found
Error message
File not found
What it means
Thrown by the Postgres bucket's openDownloadStream().toArray() when SELECT data FROM files WHERE _id = $1 returns zero rows — the requested file id is not present in the files table. This is the Postgres-backed storage adapter (used when MONGODB_URL is not set and a Postgres pool is configured) for the chat UI's file/attachment storage.
Source
Thrown at ruflo/src/ruvocal/src/lib/server/database/postgres.ts:668
data,
JSON.stringify(options?.metadata ?? {}),
]
);
},
};
}
openDownloadStream(id: ObjectId | string) {
const fileId = typeof id === "string" ? id : id.toString();
// Return a readable-like object
return {
async toArray(): Promise<Buffer[]> {
const pool = getPool();
const result = await pool.query(
`SELECT data FROM files WHERE _id = $1`,
[fileId]
);
if (result.rows.length === 0) throw new Error("File not found");
return [result.rows[0].data];
},
};
}
async delete(id: ObjectId | string) {
const fileId = typeof id === "string" ? id : id.toString();
const pool = getPool();
await pool.query(`DELETE FROM files WHERE _id = $1`, [fileId]);
}
async find(filter: Record<string, unknown> = {}) {
const w = filterToWhere(filter);
const pool = getPool();
const result = await pool.query(
`SELECT _id, filename, content_type, length, metadata, created_at FROM files WHERE ${w.text}`,
w.values
);View on GitHub (pinned to 6b01dc5a68)
Solutions
- Verify the file id against the files table and confirm the upload completed.
- If migrating, run the backfill so every referenced id exists in Postgres.
- Re-upload the file to generate a fresh id.
- Return a 404 to the client and trigger re-upload rather than letting the throw propagate.
Example fix
// before
const stream = bucket.openDownloadStream(id);
const buf = (await stream.toArray())[0]; // throws if missing
// after
const res = await pool.query('SELECT data FROM files WHERE _id = $1', [id]);
if (res.rows.length === 0) return res404('file not found');
const buf = res.rows[0].data; Defensive patterns
Strategy: try-catch
Validate before calling
async function fileExists(id: string): Promise<boolean> { const r = await pool.query('SELECT 1 FROM files WHERE _id = $1', [String(id)]); return r.rows.length > 0; } Type guard
async function hasFileRow(id: string): Promise<boolean> { const r = await pool.query('SELECT 1 FROM files WHERE _id = $1', [String(id)]); return r.rows.length > 0; } Try / catch
try { return await bucket.openDownloadStream(id).toArray(); } catch (e) { if ((e as Error).message === 'File not found') return res.status(404).json({ error: 'file not found' }); throw e; } Prevention
- Backfill files when migrating Mongo → Postgres.
- Confirm uploads committed a row before referencing the id.
- Return 404 and let the client re-upload rather than propagating the throw.
When it happens
Trigger: Downloading a file whose id has no row in the postgres files table: a stale/incorrect id, a file that expired or was deleted, a file stored in a different backend (e.g. still in Mongo) after a partial migration, or an id from another environment.
Common situations: Migrating from Mongo to Postgres left some files behind; a TTL/cleanup job removed the row; the client cached an old id; the upload failed silently so the row was never written; a cross-environment id leak (test id used in prod).
Related errors
- File not found
- User not found
- SSRF guard: invalid URL — ${rawUrl}
- SSRF guard: only HTTPS URLs are permitted, got ${parsed.prot
- SSRF guard: private/loopback host rejected — ${host}
AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12).
Data as JSON: /api/errors/ddb75fac346d1c5b.
Report an issue: GitHub.