From 0ae679553ba6ec2710f9e341d42cfcf7dacf0d22 Mon Sep 17 00:00:00 2001 From: Devvyn <22340871+opticon454@users.noreply.github.com> Date: Fri, 9 Oct 2026 07:48:25 +0800 Subject: [PATCH] fix(cases): show the real reason when Link Existing rejects a case POST /api/cases/link passed a fixed 'Invalid request body' to parseBody, which replaced the schema's own message, so a bad name and a bad folder path (a leading ~, a character a session working directory cannot have) looked the same. Drop the override so the first validation issue is returned. --- .changeset/case-link-validation-message.md | 5 +++++ src/web/routes/case-routes.ts | 2 +- test/routes/case-routes.test.ts | 19 +++++++++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 .changeset/case-link-validation-message.md diff --git a/.changeset/case-link-validation-message.md b/.changeset/case-link-validation-message.md new file mode 100644 index 00000000..267ab30c --- /dev/null +++ b/.changeset/case-link-validation-message.md @@ -0,0 +1,5 @@ +--- +'aicodeman': patch +--- + +Link Existing now says what is wrong when it rejects a case. A bad name reports "Invalid case name format" and a bad folder path reports why (not absolute, a character a session folder cannot have, ...), instead of the same "Invalid request body" for both. diff --git a/src/web/routes/case-routes.ts b/src/web/routes/case-routes.ts index 7515e1da..66a073f0 100644 --- a/src/web/routes/case-routes.ts +++ b/src/web/routes/case-routes.ts @@ -1536,7 +1536,7 @@ export function registerCaseRoutes(app: FastifyInstance, ctx: EventPort & Config // admin-only in multi-user mode (mirrors host CRUD + the admin-only GET listing). const denied = adminOnly(req, reply); if (denied) return denied; - const { name, path: folderPath } = parseBody(LinkCaseSchema, req.body, 'Invalid request body'); + const { name, path: folderPath } = parseBody(LinkCaseSchema, req.body); // Expand ~ to home directory const expandedPath = folderPath.startsWith('~') ? join(homedir(), folderPath.slice(1)) : folderPath; diff --git a/test/routes/case-routes.test.ts b/test/routes/case-routes.test.ts index c0fdcce8..c78bfa0f 100644 --- a/test/routes/case-routes.test.ts +++ b/test/routes/case-routes.test.ts @@ -647,6 +647,25 @@ describe('case-routes', () => { expect(body.success).toBe(false); }); + it('says which field is wrong instead of a generic message', async () => { + const badName = await harness.app.inject({ + method: 'POST', + url: '/api/cases/link', + payload: { name: 'bad name!', path: '/home/user/project' }, + }); + expect(badName.statusCode).toBe(400); + expect(JSON.parse(badName.body).error).toBe('Invalid case name format'); + + // A leading `~` is expanded later in the route but fails the absolute-path rule first. + const badPath = await harness.app.inject({ + method: 'POST', + url: '/api/cases/link', + payload: { name: 'my-project', path: '~/project' }, + }); + expect(badPath.statusCode).toBe(400); + expect(JSON.parse(badPath.body).error).toMatch(/^Invalid path:/); + }); + it('returns not found when folder does not exist', async () => { mockedExistsSync.mockReturnValue(false);