{"record":{"id":"0e24999848556193","repo":"paperclipai/paperclip","slug":"opencode-proxy-requires-model-in-provider-model-fo","errorCode":null,"errorMessage":"OpenCode proxy requires model in provider/model form","messagePattern":"OpenCode proxy requires model in provider/model form","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/paperclip-runner/src/cli/opencode-app-server-proxy.ts","lineNumber":96,"sourceCode":"function runtimeDirectory(): string {\n  const configured = process.env.PAPERCLIP_OPENCODE_RUNTIME_DIR?.trim();\n  if (!configured)\n    throw new Error(\"PAPERCLIP_OPENCODE_RUNTIME_DIR is required\");\n  return resolve(configured);\n}\n\nasync function open(\n  params: Record<string, unknown>,\n  resume: boolean,\n): Promise<Record<string, unknown>> {\n  if (session)\n    return threadResponse(\n      session.ids().providerSessionId ?? session.ids().driverSessionId,\n    );\n  cwd = resolve(text(params.cwd, process.cwd()));\n  const model = text(params.model);\n  if (!model.includes(\"/\"))\n    throw new Error(\"OpenCode proxy requires model in provider/model form\");\n  activeModel = model;\n  const dynamicTools = Array.isArray(params.dynamicTools)\n    ? params.dynamicTools.map(record)\n    : [];\n  const runtimeContextPath =\n    process.env.PAPERCLIP_NATIVE_RUNTIME_CONTEXT_PATH?.trim();\n  const runtimeContext = runtimeContextPath\n    ? parseNativeRuntimeContext(\n        JSON.parse(readFileSync(runtimeContextPath, \"utf8\")),\n      )\n    : null;\n  driver = new OpenCodeServerDriver({\n    model,\n    permissionMode: parseOpenCodeProxyPermissionMode(\n      process.env.PAPERCLIP_OPENCODE_PERMISSION_MODE,\n    ),\n    command: launchBinding.command,\n    commandFd: launchBinding.commandFd,","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/packages/paperclip-runner/src/cli/opencode-app-server-proxy.ts#L78-L114","documentation":"The OpenCode proxy requires the model parameter to name both provider and model in 'provider/model' form; it uses the slash as the delimiter to configure the active model, so a bare model name is rejected.","triggerScenarios":"Calling open() with params.model set to a string without a '/' (e.g. 'claude-sonnet-4', ''), when resuming is not in effect (cwd path taken).","commonSituations":"Passing just the model id from a UI dropdown that omits the provider prefix; copying model names from provider docs that show only the slug; empty model param because the field wasn't filled.","solutions":["Pass the fully qualified model, e.g. 'anthropic/claude-sonnet-4' or 'openai/gpt-5'","Prefix the bare model slug with the correct provider id before calling open","Validate the model string in the caller/UI before dispatching"],"exampleFix":"// before\nopenSession({ cwd, model: 'claude-sonnet-4' });\n// after\nopenSession({ cwd, model: 'anthropic/claude-sonnet-4' });","handlingStrategy":"validation","validationCode":"const model = String(params.model ?? '');\nif (!model.includes('/')) throw new Error('model must be provider/model, e.g. anthropic/claude-sonnet-4');","typeGuard":"function isQualifiedModel(m: unknown): m is string { return typeof m === 'string' && m.includes('/'); }","tryCatchPattern":"try { await proxy.open(params, resume); }\ncatch (e) { if (String(e.message).includes('provider/model form')) { params.model = qualifyModel(params.model); await proxy.open(params, resume); } else throw e; }","preventionTips":["Store models as qualified 'provider/model' pairs in config/UI state","Qualify bare model slugs at the boundary using the selected provider id","Add a regex/contains('/') check in UI form validation before submit"],"tags":["validation","opencode","model"],"backgroundTag":"invalid-argument-format","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}