{"record":{"id":"9cd22e60ad690c1f","repo":"decolua/9router","slug":"missing-model-9cd22e","errorCode":null,"errorMessage":"Missing model","messagePattern":"Missing model","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"src/sse/handlers/embeddings.js","lineNumber":69,"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(\"EMBEDDINGS\", \"Missing model\");\n    return errorResponse(HTTP_STATUS.BAD_REQUEST, \"Missing model\");\n  }\n\n  if (!body.input) {\n    log.warn(\"EMBEDDINGS\", \"Missing input\");\n    return errorResponse(HTTP_STATUS.BAD_REQUEST, \"Missing required field: input\");\n  }\n\n  const modelInfo = await getModelInfo(modelStr);\n  if (!modelInfo.provider) {\n    log.warn(\"EMBEDDINGS\", \"Invalid model format\", { model: modelStr });\n    return errorResponse(HTTP_STATUS.BAD_REQUEST, \"Invalid model format\");\n  }\n\n  const { provider, model } = modelInfo;\n\n  if (modelStr !== `${provider}/${model}`) {\n    log.info(\"ROUTING\", `${modelStr} → ${provider}/${model}`);","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/src/sse/handlers/embeddings.js#L51-L87","documentation":"HTTP 400 returned by handleEmbeddings when the request JSON body lacks a `model` field (or it is empty/falsy). The model string determines which provider routes the embedding request, so the handler fails fast before any credential lookup.","triggerScenarios":"POST to the embeddings endpoint with a JSON body like {\"input\":\"hello\"} or {\"model\":\"\",\"input\":\"x\"}.","commonSituations":"Client code migrated from an SDK that puts the model in the URL instead of the body; building the payload dynamically and forgetting the model; testing with curl and omitting the field.","solutions":["Add `model` to the JSON body in `provider/model` form (e.g. \"openai/text-embedding-3-small\")","Confirm the client SDK is configured with an embedding model name","Print the request body before sending to confirm the field is populated"],"exampleFix":"// before\nawait fetch(url, { method: 'POST', body: JSON.stringify({ input: ['hello'] }) });\n// after\nawait fetch(url, { method: 'POST', body: JSON.stringify({ model: 'openai/text-embedding-3-small', input: ['hello'] }) });","handlingStrategy":"validation","validationCode":"function assertEmbeddingPayload(body) {\n  if (!body || typeof body.model !== 'string' || !body.model.trim()) {\n    throw new Error('embeddings payload requires a non-empty \"model\"');\n  }\n}","typeGuard":"function hasModel(body) {\n  return typeof body === 'object' && body !== null &&\n    typeof body.model === 'string' && body.model.trim().length > 0;\n}","tryCatchPattern":"const res = await post('/v1/embeddings', payload);\nif (res.status === 400 && (await res.text()).includes('Missing model')) {\n  throw new Error('Payload missing `model` — set e.g. \"openai/text-embedding-3-small\"');\n}","preventionTips":["Always build payloads via a helper that requires model + input","Add a unit test asserting the payload schema","Keep default embedding model in config"],"tags":["validation","bad-request","embeddings"],"backgroundTag":"missing-required-field","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}