fix(rename): surface duplicate-slug 409 instead of 500 (v1.21.1)
Some checks failed
CI / Lint (push) Has been cancelled
CI / Typecheck (push) Has been cancelled
CI / Broker tests (Postgres) (push) Has been cancelled
CI / Docker build (linux/amd64) (push) Has been cancelled

mesh.slug actually carries a UNIQUE constraint (mesh_slug_unique)
even though the schema comment claimed otherwise. Trying to rename
to a slug another mesh already owns blew up as a generic 500.
Now: caught at the route, surfaced as 409 with body
{"error":"slug \"<x>\" is already taken"}; CLI maps it to
EXIT.ALREADY_EXISTS and prints the message.

Schema comment corrected to match DB reality.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alejandro Gutiérrez
2026-05-03 15:49:28 +01:00
parent 5785454ac9
commit 65e63b0b27
4 changed files with 31 additions and 13 deletions

View File

@@ -130,11 +130,26 @@ export async function PATCH(
// Soft-collapse: name and slug are a single concept user-facing,
// so we always sync name = slug. mesh.name column stays for now
// (avoids touching ~25 reader sites); a future migration drops it.
const [updated] = await db
.update(mesh)
.set({ slug: newSlug, name: newSlug })
.where(eq(mesh.slug, slug))
.returning({ slug: mesh.slug });
return NextResponse.json(updated);
//
// The DB has a unique constraint on mesh.slug (mesh_slug_unique)
// even though the schema comment incorrectly claims "NOT unique" —
// catch the duplicate-key error and surface it as 409 instead of
// a generic 500.
try {
const [updated] = await db
.update(mesh)
.set({ slug: newSlug, name: newSlug })
.where(eq(mesh.slug, slug))
.returning({ slug: mesh.slug });
return NextResponse.json(updated);
} catch (e) {
const err = e as { cause?: { constraint_name?: string; code?: string } };
if (err.cause?.constraint_name === "mesh_slug_unique" || err.cause?.code === "23505") {
return NextResponse.json(
{ error: `slug "${newSlug}" is already taken` },
{ status: 409 },
);
}
throw e;
}
}