paperclipai/paperclip · error · Error
OpenCode native backend requires an instance runtime…
Error message
OpenCode native backend requires an instance runtime directory
What it means
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.
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
Example fix
// before
const backend = createNativeSessionBackend(input, {
dynamicToolHandler,
codexTransportFactory,
});
// after
const backend = createNativeSessionBackend(input, {
dynamicToolHandler,
codexTransportFactory,
opencodeRuntimeDirectory: "/var/lib/paperclip/instances/acme/opencode",
}); Defensive patterns
Strategy: validation
Validate before calling
function canCreateOpencodeBackend(options) {
return typeof options.opencodeRuntimeDirectory === 'string' && options.opencodeRuntimeDirectory.trim().length > 0;
}
if (input.provider.kind === 'opencode' && !canCreateOpencodeBackend(options)) {
throw new Error('opencodeRuntimeDirectory must be configured before creating an opencode native backend');
} Type guard
function hasRuntimeDirectory(o) {
return typeof o.opencodeRuntimeDirectory === 'string' && o.opencodeRuntimeDirectory.trim().length > 0;
} Try / catch
let backend;
try {
backend = createNativeSessionBackend(input, options);
} catch (err) {
if (err.message.includes('instance runtime directory')) {
throw new ConfigError('Set opencodeRuntimeDirectory in runner options before using the opencode provider', { cause: err });
}
throw err;
} Prevention
- 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
When it happens
Trigger: Calling createNativeSessionBackend with input.provider.kind === 'opencode' while options.opencodeRuntimeDirectory is undefined, an empty string, or a string of only whitespace.
Common situations: 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.
Understand the failure class
Background: "missing required config value" errors: why libraries refuse to start when a configuration key is empty, unset, or blank — this error's family across 48 libraries.
Related errors
- OpenCode evals require exact version 1.18.17; received
- OpenCode working directory must not be a filesystem root
- A different semantic result was already committed
- A trusted viewer build is required for public chat reports
- ACPX provider package manifest must be an explicit…
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-02).
Data as JSON: /api/errors/0fdf5399ce6d5a75.
Report an issue: GitHub.
Appendix: source
Thrown at packages/paperclip-runner/src/backends/native-backend-factory.ts:65
export function createNativeSessionBackend(
input: NativeExecutionInput,
options: NativeBackendFactoryOptions = {},
): NativeSessionBackend {
if (options.codexTransportFactory) {
return createRunnerdNativeSessionBackend(input, {
completionFeedback: options.completionFeedback,
runnerInstanceId: options.runnerInstanceId,
onSpawn: options.onSpawn,
dynamicTools: options.dynamicTools,
dynamicToolHandler: options.dynamicToolHandler,
environment: options.environment,
workingDirectoryAuthority: options.workingDirectoryAuthority,
transportFactory: options.codexTransportFactory,
});
}
if (input.provider.kind === "opencode") {
if (!options.opencodeRuntimeDirectory?.trim()) {
throw new Error(
"OpenCode native backend requires an instance runtime directory",
);
}
return createOpenCodeNativeSessionBackend(input, {
runtimeDirectory: options.opencodeRuntimeDirectory,
environment: options.opencodeEnvironment,
command: options.opencodeCommand,
runnerInstanceId: options.runnerInstanceId,
onSpawn: options.onSpawn,
dynamicTools: options.dynamicTools,
dynamicToolHandler: options.dynamicToolHandler,
});
}
if (input.provider.kind === "acpx") {
if (input.provider.agent === "pi") {
throw new Error(
"Native ACPX backend for pi is unavailable until descriptor-confined verified launch is implemented",
);View on GitHub (pinned to 3f1d897a7c)