{"record":{"id":"cd9edc0e86869ac0","repo":"can1357/oh-my-pi","slug":"security-scan-model-is-unavailable-plan-model-p","errorCode":null,"errorMessage":"Security scan model is unavailable: ${plan.model.provider}/${plan.model.modelId}","messagePattern":"Security scan model is unavailable: (.+?)/(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/security/coordinator.ts","lineNumber":592,"sourceCode":"\t\t\t);\n\t\t\tif (signal.aborted) throw signal.reason ?? new Error(\"Security scan cancelled\");\n\t\t\tawait prepareSecurityOutputDirectory(plan.output, record.snapshot.scanId);\n\t\t\tthis.#update(record, \"preparing\");\n\t\t\tawait reportProgress?.(\"Preparing OMP-native security scan\");\n\t\t\texecutionTarget = await prepareSecurityExecutionTarget(\n\t\t\t\tplan,\n\t\t\t\tstore,\n\t\t\t\trecord.snapshot.scanId,\n\t\t\t\tthis.#gitAdapter,\n\t\t\t\tsignal,\n\t\t\t);\n\t\t\tconst activeModel = this.#host.activeModel;\n\t\t\tconst model =\n\t\t\t\tactiveModel?.provider === plan.model.provider && activeModel.id === plan.model.modelId\n\t\t\t\t\t? activeModel\n\t\t\t\t\t: this.#host.modelRegistry.find(plan.model.provider, plan.model.modelId);\n\t\t\tif (!model)\n\t\t\t\tthrow new Error(`Security scan model is unavailable: ${plan.model.provider}/${plan.model.modelId}`);\n\t\t\tconst sessionsDirectory = path.join(store.projectDirectory, \"sessions\");\n\t\t\tawait fs.mkdir(sessionsDirectory, { recursive: true, mode: 0o700 });\n\t\t\tconst sessionManager = SessionManager.create(executionTarget.cwd, sessionsDirectory);\n\t\t\tconst publicationTool = createSecurityPublicationTool({\n\t\t\t\tplan,\n\t\t\t\tscanId: record.snapshot.scanId,\n\t\t\t\tstore,\n\t\t\t\tstartedAt,\n\t\t\t\tsessionId: `security:${record.snapshot.scanId}`,\n\t\t\t\toperationId: record.snapshot.operationId,\n\t\t\t\tonPublished: async bundle => {\n\t\t\t\t\tpublishedBundle = bundle;\n\t\t\t\t\trecord.snapshot.findingCount = bundle.findings.length;\n\t\t\t\t\tthis.#update(record, \"publishing\");\n\t\t\t\t},\n\t\t\t});\n\t\t\tsession = await this.#createSession({\n\t\t\t\thost: this.#host,","sourceCodeStart":574,"sourceCodeEnd":610,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/security/coordinator.ts#L574-L610","documentation":"The security scan coordinator needs the model recorded in the scan plan to run the scan session. It first checks the host's active model, then falls back to the model registry lookup; if neither can resolve the provider/modelId pair, it throws. This guards against executing scans with a model that cannot be constructed.","triggerScenarios":"Security scan plan references a model (provider/modelId) that is neither the currently active host model nor present in the model registry — e.g. the plan was generated earlier against a model since removed from config, or the registry was built without that provider.","commonSituations":"Switching/removing a model from opencode config after a scan plan was drafted; typo'd or stale modelId in plan state; provider plugin not loaded so registry lacks the entry; running a persisted/resumed plan after a model catalog change.","solutions":["Check that the provider/model in the plan matches a model configured in the current session (the active model or one in the registry)","Re-generate the security scan plan with the currently active model so plan.model matches this.#host.activeModel","Add the missing provider/model back to the model registry/config, then retry the scan"],"exampleFix":"// before: plan holds a stale model\n{ \"model\": { \"provider\": \"anthropic\", \"modelId\": \"claude-3-opus-20240229\" } }\n// after: re-plan with the active model\nconst plan = await planScan({ model: { provider: host.activeModel.provider, modelId: host.activeModel.id } });","handlingStrategy":"validation","validationCode":"const active = host.activeModel;\nconst model = active?.provider === plan.model.provider && active.id === plan.model.modelId\n  ? active\n  : host.modelRegistry.find(plan.model.provider, plan.model.modelId);\nif (!model) throw new Error(`Plan model unavailable: ${plan.model.provider}/${plan.model.modelId} — re-plan with the active model`);","typeGuard":"function isModelResolvable(host: { activeModel?: { provider: string; id: string } | null; modelRegistry: { find(p: string, id: string): unknown } }, plan: { model: { provider: string; modelId: string } }): boolean {\n  return (host.activeModel?.provider === plan.model.provider && host.activeModel.id === plan.model.modelId) ||\n    host.modelRegistry.find(plan.model.provider, plan.model.modelId) != null;\n}","tryCatchPattern":"try {\n  await coordinator.run(plan);\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith(\"Security scan model is unavailable\")) {\n    plan = await replanWithActiveModel(); // regenerate plan against host.activeModel\n  } else throw err;\n}","preventionTips":["Regenerate scan plans from the current host.activeModel instead of persisting model references across sessions","Validate plan.model against the registry immediately after plan creation","Keep provider plugins loaded so registry lookups succeed","Log model availability when a plan is created to catch drift early"],"tags":["security","model-registry","configuration"],"backgroundTag":"model-not-found","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}