{"record":{"id":"9e12cce0a8aae0e9","repo":"decolua/9router","slug":"missing-required-field-provider-or-model-9e12cc","errorCode":null,"errorMessage":"Missing required field: provider (or model)","messagePattern":"Missing required field: provider \\(or model\\)","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"warning","filePath":"src/sse/handlers/search.js","lineNumber":62,"sourceCode":"    log.debug(\"AUTH\", \"No API key provided (local mode)\");\n  }\n\n  // Enforce API key if enabled in settings\n  const settings = await getSettings();\n  if (settings.requireApiKey) {\n    if (!apiKey) {\n      log.warn(\"AUTH\", \"Missing API key (requireApiKey=true)\");\n      return errorResponse(HTTP_STATUS.UNAUTHORIZED, \"Missing API key\");\n    }\n    const valid = await isValidApiKey(apiKey);\n    if (!valid) {\n      log.warn(\"AUTH\", \"Invalid API key (requireApiKey=true)\");\n      return errorResponse(HTTP_STATUS.UNAUTHORIZED, \"Invalid API key\");\n    }\n  }\n\n  if (!providerInput || typeof providerInput !== \"string\") {\n    log.warn(\"SEARCH\", \"Missing provider/model\");\n    return errorResponse(HTTP_STATUS.BAD_REQUEST, \"Missing required field: provider (or model)\");\n  }\n\n  if (!query || typeof query !== \"string\" || !query.trim()) {\n    log.warn(\"SEARCH\", \"Missing query\");\n    return errorResponse(HTTP_STATUS.BAD_REQUEST, \"Missing required field: query\");\n  }\n\n  // Combo expansion: providerInput may be a combo name → run fallback/round-robin across providers\n  const combos = await getCombos();\n  const comboModels = getComboModelsFromData(providerInput, combos);\n  if (comboModels) {\n    const comboStrategies = settings.comboStrategies || {};\n    const comboStrategy = comboStrategies[providerInput]?.fallbackStrategy || settings.comboStrategy || \"fallback\";\n    const comboStickyLimit = settings.comboStickyRoundRobinLimit;\n    log.info(\"SEARCH\", `Combo \"${providerInput}\" with ${comboModels.length} providers (strategy: ${comboStrategy}, sticky: ${comboStickyLimit})`);\n    return handleComboChat({\n      body,","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/src/sse/handlers/search.js#L44-L80","documentation":"handleSearch validates the request body before dispatching a web-search request. The body must contain either `provider` or `model` (they are synonyms; provider IS the model for web search). If neither is a non-empty string, the handler returns HTTP 400 'Missing required field: provider (or model)'. This is an input-validation guard, not a runtime failure.","triggerScenarios":"POST /v1/search (or the search endpoint mapped to handleSearch) with a JSON body lacking both `provider` and `model`, containing them as non-string values (e.g. null, number, object), or as an empty string. Also happens when a client serializes the provider under a wrong key name.","commonSituations":"Hand-written curl scripts omitting the field; SDK clients built for the chat endpoint (which uses `model` inside a nested payload) posting the wrong shape; a UI bug sending `model: null` after clearing a selection; middleware that strips fields; sending provider as a query param instead of in the JSON body.","solutions":["Add `provider` (or `model`) as a non-empty string to the JSON body of the request.","Verify the field is sent in the JSON body, not as a URL query parameter or header.","Check the client is not posting an empty body / wrong Content-Type causing fields to be dropped.","If proxying, ensure upstream middleware does not strip or rename the provider/model field."],"exampleFix":"// before\nawait fetch('/v1/search', { method: 'POST', body: JSON.stringify({ query: 'rust async' }) });\n// after\nawait fetch('/v1/search', { method: 'POST', body: JSON.stringify({ provider: 'tavily', query: 'rust async' }) });","handlingStrategy":"validation","validationCode":"const provider = body.provider || body.model;\nif (typeof provider !== 'string' || !provider) {\n  throw new TypeError('provider (or model) is required and must be a non-empty string');\n}","typeGuard":"function hasProvider(b) { return typeof b === 'object' && b !== null && typeof (b.provider ?? b.model) === 'string' && (b.provider ?? b.model).length > 0; }","tryCatchPattern":null,"preventionTips":["Validate request bodies with a schema (zod/ajv) before hitting the endpoint.","Standardize on `model` across your clients since the API accepts it as an alias.","Always send Content-Type: application/json so body parsing succeeds."],"tags":["http-400","validation","web-search","request-body"],"backgroundTag":"missing-required-field","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}