{"record":{"id":"0fdf5399ce6d5a75","repo":"paperclipai/paperclip","slug":"opencode-native-backend-requires-an-instance-runti","errorCode":null,"errorMessage":"OpenCode native backend requires an instance runtime directory","messagePattern":"OpenCode native backend requires an instance runtime directory","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/paperclip-runner/src/backends/native-backend-factory.ts","lineNumber":54,"sourceCode":" * Persisted contracts for future providers do not make those providers\n * executable before their independently reviewed runtime ships.\n */\nexport function createNativeSessionBackend(\n  input: NativeExecutionInput,\n  options: NativeBackendFactoryOptions = {},\n): NativeSessionBackend {\n  if (options.codexTransportFactory) {\n    return createRunnerdNativeSessionBackend(input, {\n      runnerInstanceId: options.runnerInstanceId,\n      onSpawn: options.onSpawn,\n      dynamicTools: options.dynamicTools,\n      dynamicToolHandler: options.dynamicToolHandler,\n      transportFactory: options.codexTransportFactory,\n    });\n  }\n  if (input.provider.kind === \"opencode\") {\n    if (!options.opencodeRuntimeDirectory?.trim()) {\n      throw new Error(\n        \"OpenCode native backend requires an instance runtime directory\",\n      );\n    }\n    return createOpenCodeNativeSessionBackend(input, {\n      runtimeDirectory: options.opencodeRuntimeDirectory,\n      environment: options.opencodeEnvironment,\n      command: options.opencodeCommand,\n      runnerInstanceId: options.runnerInstanceId,\n      onSpawn: options.onSpawn,\n      dynamicTools: options.dynamicTools,\n      dynamicToolHandler: options.dynamicToolHandler,\n    });\n  }\n  if (input.provider.kind === \"acpx\") {\n    if (input.provider.agent === \"pi\") {\n      throw new Error(\n        \"Native ACPX backend for pi is unavailable until descriptor-confined verified launch is implemented\",\n      );","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/paperclipai/paperclip/blob/5716fe907e596ce73501408fc6efdb19fb61edf2/packages/paperclip-runner/src/backends/native-backend-factory.ts#L36-L72","documentation":"createNativeSessionBackend wires up native session backends per provider. For the 'opencode' provider it needs a dedicated per-instance runtime directory where the OpenCode native backend stores its session/state files. If options.opencodeRuntimeDirectory is missing, empty, or whitespace-only, the factory refuses to build the backend and throws this error instead of creating a misconfigured session.","triggerScenarios":"Calling createNativeSessionBackend with input.provider.kind === 'opencode' while options.opencodeRuntimeDirectory is undefined, an empty string, or a string of only whitespace.","commonSituations":"Operator config omits the opencode runtime directory setting; a templated config resolves the directory to an empty string; environment-specific wiring passes options through without the opencode block populated; a new instance was provisioned without its runtime directory being created/passed.","solutions":["Set options.opencodeRuntimeDirectory to an absolute, existing directory path dedicated to this instance","Verify the config source that populates opencodeRuntimeDirectory is actually supplying a value for opencode providers","Trim/default the value at config-load time so it cannot arrive as '' or whitespace","Only route opencode-provider sessions through this factory once the runtime directory is provisioned"],"exampleFix":"// before\nconst backend = createNativeSessionBackend(input, {\n  dynamicToolHandler,\n  codexTransportFactory,\n});\n// after\nconst backend = createNativeSessionBackend(input, {\n  dynamicToolHandler,\n  codexTransportFactory,\n  opencodeRuntimeDirectory: \"/var/lib/paperclip/instances/acme/opencode\",\n});","handlingStrategy":"validation","validationCode":"function canCreateOpencodeBackend(options) {\n  return typeof options.opencodeRuntimeDirectory === 'string' && options.opencodeRuntimeDirectory.trim().length > 0;\n}\nif (input.provider.kind === 'opencode' && !canCreateOpencodeBackend(options)) {\n  throw new Error('opencodeRuntimeDirectory must be configured before creating an opencode native backend');\n}","typeGuard":"function hasRuntimeDirectory(o) {\n  return typeof o.opencodeRuntimeDirectory === 'string' && o.opencodeRuntimeDirectory.trim().length > 0;\n}","tryCatchPattern":"let backend;\ntry {\n  backend = createNativeSessionBackend(input, options);\n} catch (err) {\n  if (err.message.includes('instance runtime directory')) {\n    throw new ConfigError('Set opencodeRuntimeDirectory in runner options before using the opencode provider', { cause: err });\n  }\n  throw err;\n}","preventionTips":["Centralize runner options construction so opencodeRuntimeDirectory is always populated from instance config","Fail fast at config load time: validate required directories before the runner starts","Use absolute paths and never pass template strings that can resolve to empty","Add a startup assertion/log when the opencode provider is enabled without a runtime directory"],"tags":["configuration","opencode","runtime-directory"],"backgroundTag":"missing-config-value","analyzedSha":"5716fe907e596ce73501408fc6efdb19fb61edf2","analyzedAt":"2026-09-02T18:44:00.616Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}