amruthpillai/reactive-resume · warning · ORPCError
NOT_FOUND
NOT_FOUND
Error message
NOT_FOUND
What it means
Thrown after storageService.delete(key) returns false, meaning no file or directory existed at the resolved path. The ownership and traversal checks already passed (otherwise FORBIDDEN would have fired first). oRPC raises NOT_FOUND (HTTP 404) — this is a benign 'nothing to delete' outcome.
Source
Thrown at packages/api/src/features/storage/router.ts:103
FORBIDDEN: {
message: "You do not have permission to delete this file.",
status: 403,
},
})
.handler(async ({ context, input }): Promise<void> => {
const requestedKey = normalizeKey(input.filename);
const key = requestedKey.startsWith("uploads/")
? requestedKey
: normalizeKey(`uploads/${context.user.id}/pictures/${requestedKey}`);
const userPrefix = `uploads/${context.user.id}/`;
if (isUnsafeStorageKey(key) || !key.startsWith(userPrefix)) {
throw new ORPCError("FORBIDDEN");
}
const deleted = await storageService.delete(key);
if (!deleted) throw new ORPCError("NOT_FOUND");
}),
};
View on GitHub (pinned to 3a5b12e2a4)
Solutions
- Treat 404 from deleteFile as success (idempotent delete) in the client and clear the local reference.
- Invalidate the relevant query cache so the UI stops offering the missing file for deletion.
- Avoid offering a delete action for paths that are no longer present in the server's listing.
- If idempotent semantics are desired server-side, consider returning 200 instead of 404 — but do not change behavior without coordinating API consumers.
Example fix
// before: surfacing 404 as an error on delete
try { await orpc.storage.deleteFile.mutate({ filename }); }
catch (e) { toast.error('Delete failed'); }
// after: treat 'already gone' as success
try { await orpc.storage.deleteFile.mutate({ filename }); }
catch (e) {
if (!isORPCError(e, 'NOT_FOUND')) throw e;
}
queryClient.invalidateQueries({ queryKey: ['storage'] }); Defensive patterns
Strategy: try-catch
Validate before calling
const mine = await orpc.storage.list.query({ prefix: `uploads/${userId}/` });
if (!mine.includes(filename)) { /* already gone — treat as deleted */ } Type guard
function isStorageNotFound(e: unknown): boolean {
return e instanceof ORPCError && e.code === 'NOT_FOUND';
} Try / catch
try { await orpc.storage.deleteFile.mutate({ filename }); }
catch (e) { if (!isStorageNotFound(e)) throw e; /* idempotent: already deleted */ } Prevention
- Treat delete 404 as success; do not alarm the user.
- Invalidate the file-list cache after delete so the UI drops the entry.
- Disable the delete affordance once the path is no longer listed.
When it happens
Trigger: Deleting a file that was already deleted; a stale URL/path from an older upload; the picture row was cleared but the client still holds the old path; concurrent delete requests racing on the same key.
Common situations: User removes a profile picture, clicks delete again; cached UI referencing an uploaded file that a background cleanup reaped; retry of a delete that already succeeded.
Related errors
AI-assisted analysis of amruthpillai/reactive-resume@3a5b12e2a4 (2026-08-12).
Data as JSON: /api/errors/dae3ebae30689c38.
Report an issue: GitHub.