Return 400 for malformed document id instead of 500 (PAP-10582)

A non-UUID :documentId reached a uuid-typed equality query and surfaced
Postgres's "invalid input syntax for type uuid" as an opaque 500. Add a
router.param guard that rejects malformed ids early with a structured 400,
covering every :documentId route at once.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
DottaandClaude Opus 4.8 committed 2026-06-07 03:38:57 +00:00
1 parent 97ec26b11d
commit eac2752227
2 files changed
+36

No files matched your search

@@ -158,4 +158,28 @@ describeEmbeddedPostgres("document routes", () => {
}),
]));
});
it("returns 400 for a malformed document id instead of a 500 (PAP-10582)", async () => {
const companyA = await seedCompany("A");
const app = createApp();
const res = await request(app)
.get(`/api/companies/${companyA}/documents/not-a-uuid`)
.expect(400);
expect(res.body).toEqual({ error: "Invalid document id format" });
// Other :documentId routes are covered by the same param guard.
await request(app)
.get(`/api/companies/${companyA}/documents/not-a-uuid/backlinks`)
.expect(400);
await request(app)
.patch(`/api/companies/${companyA}/documents/not-a-uuid`)
.send({ status: "archived" })
.expect(400);
// A well-formed but non-existent id still resolves to a 404, not a 400.
await request(app)
.get(`/api/companies/${companyA}/documents/${randomUUID()}`)
.expect(404);
});
});
+12
View File
@@ -3,6 +3,7 @@ import type { Db } from "@paperclipai/db";
import {
companyDocumentListQuerySchema,
createDocumentLinkSchema,
isUuidLike,
updateDocumentMetadataSchema,
} from "@paperclipai/shared";
import { validate } from "../middleware/validate.js";
@@ -15,6 +16,17 @@ export function documentRoutes(db: Db) {
const projects = projectService(db);
const access = accessService(db);
// Document ids are UUID-keyed. Reject malformed ids early so they surface as a
// structured 400 instead of bubbling up as a Postgres "invalid input syntax for
// type uuid" error (which would become an opaque 500). See PAP-10582.
router.param("documentId", (req, res, next, value) => {
if (!isUuidLike(value)) {
res.status(400).json({ error: "Invalid document id format" });
return;
}
next();
});
router.get("/companies/:companyId/documents", async (req, res) => {
const companyId = req.params.companyId as string;
assertCompanyAccess(req, companyId);