{"record":{"id":"55b0b4e8654c13c1","repo":"decolua/9router","slug":"qoder-model-config-for-qoderkey-not-yet-know","errorCode":null,"errorMessage":"qoder: model_config for \"${qoderKey}\" not yet known (run a model list fetch or check upstream connectivity)","messagePattern":"qoder: model_config for \"(.+?)\" not yet known \\(run a model list fetch or check upstream connectivity\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"open-sse/executors/qoder.js","lineNumber":142,"sourceCode":"  return s && s.length > n ? `${s.slice(0, n)}...` : s || \"\";\n}\n\n/**\n * Map the OpenAI-style request body into the exact shape Qoder expects.\n */\nasync function buildQoderRequestBody({ model, body, credentials, log, proxyOptions, signal }) {\n  const qoderKey = String(model || \"\").replace(/^qoder\\//, \"\");\n  \n  // Fetch model config from dynamic API instead of relying on static QODER_MODEL_MAP.\n  // This allows support for new Qoder models (e.g., qmodel_latest) without code changes.\n  let modelConfig = await getQoderModelConfig(credentials, qoderKey, { log, proxyOptions, signal });\n  if (!modelConfig) {\n    // Try a forced refresh once before giving up — the cache may simply\n    // not be populated yet on first ever call for this credential.\n    const refreshed = await resolveQoderModels(credentials, { forceRefresh: true, log, proxyOptions, signal });\n    const retried = refreshed?.rawConfigs.get(qoderKey);\n    if (!retried) {\n      throw new Error(\n        `qoder: model_config for \"${qoderKey}\" not yet known (run a model list fetch or check upstream connectivity)`,\n      );\n    }\n    modelConfig = { ...retried, key: qoderKey };\n  }\n\n  const { messages, systemText } = normalizeMessages(body.messages || []);\n  const tools = body.tools;\n  const isReasoning = !!modelConfig.is_reasoning;\n  const maxOutputTokens = Number(modelConfig.max_output_tokens) || 0;\n\n  let maxTokens = 32_768;\n  if (maxOutputTokens > 0) maxTokens = maxOutputTokens;\n  if (typeof body.max_tokens === \"number\" && body.max_tokens > 0 && body.max_tokens < maxTokens) {\n    maxTokens = body.max_tokens;\n  }\n  if (typeof body.max_completion_tokens === \"number\" && body.max_completion_tokens > 0 && body.max_completion_tokens < maxTokens) {\n    maxTokens = body.max_completion_tokens;","sourceCodeStart":124,"sourceCodeEnd":160,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/open-sse/executors/qoder.js#L124-L160","documentation":"Qoder's executor builds request bodies from model metadata (is_reasoning, max_output_tokens, model key) fetched dynamically from Qoder's model-list API and cached per credential. buildQoderRequestBody() looks up the requested model key; if the cache misses it forces one refresh, and if the key is still absent it throws this error because it cannot construct a valid upstream request for an unknown model.","triggerScenarios":"A request targets a Qoder model whose key is not present in the freshly fetched model list: typo'd/renamed model id (e.g. qmodel_latest vs qmodel-latest), a brand-new upstream model the deployed resolver can't map, upstream connectivity failure during both cached and forced refresh, or an expired/invalid credential making the model-list call fail silently.","commonSituations":"Client (e.g. Claude Code/Cursor) configured with a model alias that no longer exists upstream; first-ever call before any model fetch succeeded plus a network outage; Qoder retiring or renaming models; a proxy blocking the model-list endpoint.","solutions":["Run a model list fetch (GET the router's /v1/models for qoder) to see the valid model keys and correct the client's model name","Verify upstream connectivity for the credential — the error explicitly suggests checking connectivity; test the Qoder model-list endpoint with the credential","Update 9router in case the model was added upstream and needs a newer alias map in config/providerModels.js or the resolver","Re-authenticate the Qoder credential if the model-list call fails with auth errors","Retry once connectivity is restored — the forced refresh will then populate the cache"],"exampleFix":"// before\nmodel: 'qoder/qmodel_latest_v3' // guessed name\n// after\nmodel: 'qoder/qmodel-latest'   // exact key from GET /v1/models","handlingStrategy":"validation","validationCode":"const models = await fetch('/v1/models').then(r => r.json());\nconst valid = models.data.some(m => m.id === 'qoder/qmodel-latest');\nif (!valid) throw new Error('pick a model id from GET /v1/models');","typeGuard":null,"tryCatchPattern":"try {\n  await executor.execute(req);\n} catch (e) {\n  if (String(e.message).includes('not yet known')) {\n    // refresh qoder model list, then retry with a valid key\n    await refreshQoderModels();\n    return retryWithCorrectedModel(req);\n  }\n  throw e;\n}","preventionTips":["Always source model ids from the router's /v1/models, not guesses","Prime the qoder model cache with a list fetch before first traffic","Keep credentials valid — the model-list call needs working auth","Update 9router when new upstream models appear"],"tags":["qoder","model-config","cache-miss","model-list","connectivity"],"backgroundTag":"unknown-model","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}