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

  1. Treat 404 from deleteFile as success (idempotent delete) in the client and clear the local reference.
  2. Invalidate the relevant query cache so the UI stops offering the missing file for deletion.
  3. Avoid offering a delete action for paths that are no longer present in the server's listing.
  4. 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

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.