diff --git a/src/client/routes/admin/tags/index.tsx b/src/client/routes/admin/tags/index.tsx
index e70ee1a..ffd3d08 100644
--- a/src/client/routes/admin/tags/index.tsx
+++ b/src/client/routes/admin/tags/index.tsx
@@ -124,8 +124,10 @@ function AdminTagsPage() {
});
setNewName("");
setNewParentId(null);
- } catch {
- setCreateError("Failed to create tag. Please try again.");
+ } catch (err: unknown) {
+ const message =
+ err instanceof Error ? err.message : "Failed to create tag";
+ setCreateError(message);
}
}
@@ -156,39 +158,50 @@ function AdminTagsPage() {
{/* Quick-add form */}
-
- {createError && (
- {createError}
- )}
+
+
+ Add Tag
+
+
+ {createError && (
+
{createError}
+ )}
+
{/* Error state */}
{isError && (
diff --git a/src/server/routes/admin-tags.ts b/src/server/routes/admin-tags.ts
index 4683df4..8c466b3 100644
--- a/src/server/routes/admin-tags.ts
+++ b/src/server/routes/admin-tags.ts
@@ -45,8 +45,22 @@ app.get("/:id", async (c) => {
app.post("/", zValidator("json", createTagSchema), async (c) => {
const db = c.get("db");
const data = c.req.valid("json");
- const tag = await createTag(db, data);
- return c.json(tag, 201);
+ try {
+ const tag = await createTag(db, data);
+ return c.json(tag, 201);
+ } catch (err) {
+ if (
+ err instanceof Error &&
+ (err.message.includes("UNIQUE constraint failed") ||
+ err.message.includes("unique constraint"))
+ ) {
+ return c.json(
+ { error: `A tag named "${data.name}" already exists` },
+ 409,
+ );
+ }
+ throw err;
+ }
});
// PUT /api/admin/tags/:id — rename and/or reparent a tag