diff --git a/src/process/RegexMatcher.ts b/src/process/RegexMatcher.ts index ff46be6f9d..9e98e4baeb 100644 --- a/src/process/RegexMatcher.ts +++ b/src/process/RegexMatcher.ts @@ -555,9 +555,19 @@ export const matchRegexValues = async (input: { return finish( err( input.invalidPattern?.(reply.data.reason) ?? - new AnalysisInputError(input.operation, { - userMessage: `Invalid regex pattern: ${reply.data.reason}`, - }), + new AnalysisInputError( + input.operation, + { + userMessage: `Invalid regex pattern: ${reply.data.reason}`, + }, + [ + { + path: ["pattern"], + reason: "invalid_format", + message: "Supply a valid regular expression.", + }, + ], + ), ), ); if ( diff --git a/tests/boundary/process/regexMatcher.test.ts b/tests/boundary/process/regexMatcher.test.ts index b5e735e19c..7ffe8dcdf7 100644 --- a/tests/boundary/process/regexMatcher.test.ts +++ b/tests/boundary/process/regexMatcher.test.ts @@ -502,6 +502,35 @@ it("preserves Unicode matching order and refuses a pre-cancelled match", async ( ).toMatchObject({ ok: false, error: { _tag: "AnalysisCancelledError" } }); }); +it("names the rejected pattern field for a default invalid-pattern refusal", async () => { + const matched = await matchRegexValues({ + operation: "search_procedures", + pattern: "[", + caseSensitive: false, + values: ["value"], + }); + expect(matched.ok).toBe(false); + if (matched.ok) return; + expect(projectAnalysisError(matched.error)).toMatchObject({ + code: "invalid_request", + category: "invalid_input", + recovery: { + kind: "inspect_input_contract", + operation: "search_procedures", + }, + details: { + operation: "search_procedures", + issues: [ + { + path: ["pattern"], + reason: "invalid_format", + message: "Supply a valid regular expression.", + }, + ], + }, + }); +}); + it("keeps Hopper's result projection and invalid-pattern contract with the shared worker", async () => { const search = new HopperRegexSearch({ callTool: async () => diff --git a/tests/boundary/providers/kuna/kunaStrings.test.ts b/tests/boundary/providers/kuna/kunaStrings.test.ts index 4d1348e954..6454fc27df 100644 --- a/tests/boundary/providers/kuna/kunaStrings.test.ts +++ b/tests/boundary/providers/kuna/kunaStrings.test.ts @@ -94,7 +94,13 @@ it("shares a typed string scan across concurrent searches and exact address read }); expect( await client.execute("search_strings", { pattern: "[", mode: "regex" }), - ).toMatchObject({ ok: false, error: { _tag: "AnalysisInputError" } }); + ).toMatchObject({ + ok: false, + error: { + _tag: "AnalysisInputError", + issues: [{ path: ["pattern"], reason: "invalid_format" }], + }, + }); expect((await run(client, "list_strings")).result).toHaveLength(2); } finally { expect(await client.close()).toEqual({ ok: true, value: null });