{"record":{"id":"7f00687bf467f4c5","repo":"JuliusBrussee/caveman","slug":"qwen-routed-profile-is-missing-key","errorCode":null,"errorMessage":"Qwen routed profile is missing ${key}","messagePattern":"Qwen routed profile is missing (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/cli/src/index.ts","lineNumber":8878,"sourceCode":"      // whenever native registration + journal + effective policy do not agree.\n      const configured = jsonValueAt(baseConfig, [\"mcp\", \"excluded\"]);\n      const preserved = Array.isArray(configured)\n        ? configured.filter((item): item is string => typeof item === \"string\")\n        : [];\n      rawOverlay = deepMerge(rawOverlay, {\n        mcp: { excluded: [...new Set([...preserved, \"caveman\"])] },\n      });\n    }\n  }\n  const overlay = (mcpMode === \"auto\" ? rawOverlay : withoutCavemanMcpServer(rawOverlay as JsonObject)) as JsonObject;\n  const merged = deepMerge(baseConfig, overlay) as JsonObject;\n  if (agent.id === \"qwen\") {\n    // Qwen treats these as whole-object REPLACE settings. Our generic merge is\n    // intentionally conservative for other agents, but retaining a sibling\n    // provider here would make it selectable at runtime outside /w/qwen.\n    for (const key of [\"modelProviders\", \"providerProtocol\"] as const) {\n      const value = overlay[key];\n      if (value === undefined) throw new Error(`Qwen routed profile is missing ${key}`);\n      merged[key] = cloneJsonValue(value);\n    }\n  }\n  const rendered = JSON.stringify(merged, null, 2);\n  if (rendered === undefined) throw new Error(\"merged config is not JSON\");\n  const outDir = mkdtempSync(join(tmpdir(), \"caveman-wrap-\"));\n  const outPath = join(outDir, `${agent.id}.json`);\n  writeFileSync(outPath, rendered + \"\\n\", { mode: 0o600 });\n  wrapTempDirs.add(outDir);\n  env[inj.env_var] = outPath;\n}\n\nfunction cleanupWrapTempDirs() {\n  for (const dir of [...wrapTempDirs]) {\n    try {\n      rmSync(dir, { recursive: true, force: true });\n    } catch {\n      // Temp cleanup is best-effort; wrap must never fail after child exit.","sourceCodeStart":8860,"sourceCodeEnd":8896,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/5184b3d11ac6a1acb7d44b9bfaa31698157cff97/packages/cli/src/index.ts#L8860-L8896","documentation":"After merging the rendered profile overlay into Qwen's base config, the CLI asserts that the overlay itself defines both modelProviders and providerProtocol, because Qwen treats these as whole-object REPLACE settings — deep-merging would leave sibling providers selectable at runtime outside the /w/qwen route. If either key is absent from the overlay (a broken/trimmed profile template or an overlay builder that didn't emit it), this invariant throws.","triggerScenarios":"Running `caveman wrap qwen` (config-file injection path) where the rendered overlay object lacks `modelProviders` or `providerProtocol` — e.g. a managed/local profile template from the registry is missing these keys, an overlay builder produced a partial overlay, or a corrupted/customized profile JSON dropped them.","commonSituations":"Outdated CLI talking to a newer control plane whose profile overlay schema changed (or vice versa); locally edited or partially synced profile templates; custom builds where overlayBuilders.qwen was replaced with a thinner overlay.","solutions":["Update the caveman CLI to the latest version so the bundled Qwen overlay templates match the installed profile schema.","Re-fetch/re-sync your project profiles (`caveman sync`) so the managed/local config_overlay templates are complete.","If you maintain a custom overlay builder for qwen, ensure it always emits both modelProviders and providerProtocol as whole objects.","Inspect the rendered overlay (temporary settings written under a `caveman-wrap-*` temp dir) to confirm which key is missing and fix the source template."],"exampleFix":"// before: custom qwen overlay missing replace-keys\n{ model: { primary: \"caveman/qwen3-coder\" } }\n// after: both REPLACE settings present\n{ model: { primary: \"caveman/qwen3-coder\" }, modelProviders: { ... }, providerProtocol: \"openai-completions\" }","handlingStrategy":"validation","validationCode":"// Validate the qwen overlay before relying on it\nconst overlay = profile.config_overlay.managed ?? profile.config_overlay.local;\nfor (const key of [\"modelProviders\", \"providerProtocol\"]) {\n  if (overlay?.[key] === undefined) throw new Error(`qwen profile overlay missing ${key}; re-sync profiles`);\n}","typeGuard":"function qwenOverlayIsComplete(o: unknown): o is { modelProviders: object; providerProtocol: string } & Record<string, unknown> {\n  return typeof o === \"object\" && o !== null\n    && (o as any).modelProviders !== undefined\n    && (o as any).providerProtocol !== undefined;\n}","tryCatchPattern":"try {\n  execSync(\"caveman qwen\", { stdio: \"inherit\" });\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith(\"Qwen routed profile is missing\")) {\n    console.error(\"Profile overlay incomplete — run `caveman sync` or upgrade the CLI.\");\n    process.exit(1);\n  }\n  throw e;\n}","preventionTips":["Keep the CLI and synced profiles on matching versions (`caveman sync` after upgrades).","Don't hand-edit managed profile overlay templates.","If you supply a custom qwen overlay builder, add a test asserting both REPLACE keys are present.","Diff your local profile JSON against a fresh download when this error appears after edits."],"tags":["schema-validation","profile-overlay","qwen","invariant","cli"],"backgroundTag":"missing-required-config-field","analyzedSha":"5184b3d11ac6a1acb7d44b9bfaa31698157cff97","analyzedAt":"2026-09-06T12:00:26.372Z","contentChangedAt":"2026-09-06T12:00:26.372Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}