Merge pull request #10919 from diegosouzapw/fix/10849-search-provider-400

fix(api): POST /v1/search names unknown providers instead of opaque 400 (#10849)
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-08-20 21:19:31 -03:00
committed by GitHub
8 changed files with 138 additions and 28 deletions

View File

@@ -56,6 +56,19 @@ export function isValidationFailure<TData>(
return validation.success === false;
}
/**
* Build a human-readable 400 message from a validation failure, naming the
* first offending field instead of the generic "Invalid request" (#10849).
* Intended for routes that reply with a single message string (e.g.
* `errorResponse()`) rather than the full `{ message, details }` envelope
* returned by `validatedJsonBody()`.
*/
export function formatValidationMessage(error: ValidationErrorPayload): string {
const [first] = error.details;
if (!first) return error.message;
return first.field ? `${first.field}: ${first.message}` : first.message;
}
/**
* Result of attempting to parse and validate a JSON body against a Zod schema.
*

View File

@@ -568,27 +568,16 @@ export const v1SearchSchema = z
.trim()
.min(1, "Query is required")
.max(500, "Query must be 500 characters or fewer"),
provider: z
.enum([
"serper-search",
"brave-search",
"perplexity-search",
"exa-search",
"tavily-search",
"firecrawl",
"google-pse-search",
"linkup-search",
"ollama-search",
"searchapi-search",
"youcom-search",
"searxng-search",
"zai-search",
"jina-search",
"jina-ai",
"jina",
"duckduckgo-free",
])
.optional(),
// Not a z.enum: the runtime catalog (SEARCH_PROVIDERS + SEARCH_PROVIDER_ALIASES in
// open-sse/config/searchRegistry.ts) is the source of truth via resolveSearchProvider(),
// which already returns a named "Unknown search provider: <id>" error for bad ids (see
// src/app/api/v1/search/route.ts). A hard-coded enum here would 400 before that check
// ever runs, hiding the informative message behind a generic Zod failure (#10849).
// Known catalog ids as of this writing: serper-search, brave-search, perplexity-search,
// exa-search, tavily-search, firecrawl, google-pse-search, linkup-search, ollama-search,
// searchapi-search, youcom-search, searxng-search, zai-search, jina-search, jina-ai,
// jina, duckduckgo-free (plus short aliases resolved by SEARCH_PROVIDER_ALIASES).
provider: z.string().min(1).optional(),
max_results: z.coerce.number().int().min(1).max(100).default(5),
search_type: z.enum(["web", "news"]).default("web"),
offset: z.coerce.number().int().min(0).default(0),