{"record":{"id":"2e4763de13f50540","repo":"decolua/9router","slug":"missing-model","errorCode":null,"errorMessage":"Missing model","messagePattern":"Missing model","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"src/sse/handlers/chat.js","lineNumber":79,"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 (!modelStr) {\n    log.warn(\"CHAT\", \"Missing model\");\n    return errorResponse(HTTP_STATUS.BAD_REQUEST, \"Missing model\");\n  }\n\n  // Bypass naming/warmup requests before combo rotation to avoid wasting rotation slots\n  const userAgent = request?.headers?.get(\"user-agent\") || \"\";\n  const bypassResponse = handleBypassRequest(body, modelStr, userAgent, !!settings.ccFilterNaming);\n  if (bypassResponse) return bypassResponse.response || bypassResponse;\n\n  const requiredCapabilities = detectRequiredCapabilities(body);\n\n  // Check if model is a combo (has multiple models with fallback)\n  const comboModels = await getComboModels(modelStr);\n  if (comboModels) {\n    // Check for combo-specific strategy first, fallback to global\n    const comboStrategies = settings.comboStrategies || {};\n    const comboSpecificStrategy = comboStrategies[modelStr]?.fallbackStrategy;\n    const comboStrategy = comboSpecificStrategy || settings.comboStrategy || \"fallback\";\n    const augmentedModels = augmentModelsWithCapacityAdapter(comboModels, requiredCapabilities, settings);","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/src/sse/handlers/chat.js#L61-L97","documentation":"The parsed request body has no 'model' field, which the gateway needs to resolve a provider or combo. After auth, handleChat checks body.model and returns 400 'Missing model' when it is undefined/empty.","triggerScenarios":"POST to /v1/chat/completions with JSON that lacks body.model, e.g. {\"messages\":[...]} or {\"model\":\"\",\"messages\":[...]}.","commonSituations":"Hand-written payloads or scripts that build the request object and forget the model; clients where the model parameter is set conditionally; requests forwarded through middleware that strips unknown fields; using an embeddings-style payload shape for chat.","solutions":["Add the model field: {\"model\":\"<provider/model or combo name>\", \"messages\":[...]}.","Log the outgoing payload before sending and confirm model is a non-empty string.","Use a model name listed in the 9Router dashboard (provider/model or a configured combo).","If middleware rewrites the body, ensure it preserves the model field."],"exampleFix":"// before\nawait client.chat.create({ messages });\n\n// after\nawait client.chat.create({ model: 'openai/gpt-4o', messages });","handlingStrategy":"validation","validationCode":"if (!body || typeof body.model !== 'string' || !body.model.trim()) {\n  throw new TypeError(\"chat request requires a non-empty 'model' field\");\n}","typeGuard":"function hasModel(body) {\n  return Boolean(body) && typeof body === 'object' &&\n    typeof body.model === 'string' && body.model.trim().length > 0;\n}","tryCatchPattern":"const res = await fetch(url, opts);\nif (res.status === 400 && (await res.text()).includes('Missing model')) {\n  throw new Error('Request payload is missing body.model');\n}","preventionTips":["Always include model in chat payloads; make it a required parameter in your wrapper functions.","Validate payloads with a schema (zod/ajv) before sending.","Keep model names in a central constants file to avoid conditional omission.","Log the outgoing JSON during integration debugging."],"tags":["http","bad-request","validation","model"],"backgroundTag":"missing-required-field","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}