{"record":{"id":"60551e42af0b5de7","repo":"paperclipai/paperclip","slug":"plugin-worker-pluginid-is-not-running-for-the","errorCode":null,"errorMessage":"Plugin worker \"${pluginId}\" is not running for the duplex channel.","messagePattern":"Plugin worker \"(.+?)\" is not running for the duplex channel\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/src/services/environment-runtime.ts","lineNumber":2543,"sourceCode":"        metadataConfig,\n        { issueId: input.lease.issueId, heartbeatRunId: input.lease.heartbeatRunId },\n      );\n      await destroySandboxProviderLease({\n        config: cleanupConfig,\n        providerLeaseId: input.lease.providerLeaseId,\n      });\n    },\n\n    async isPendingCleanupWorkerReady(input) {\n      // Resolve the recorded provider the same way `retryPendingSandboxTeardown`\n      // does, so the probe reads the same target the teardown uses.\n      const recordedProvider =\n        input.lease.provider ??\n        (typeof input.lease.metadata?.provider === \"string\" ? input.lease.metadata.provider : null);\n      // A missing provider is a permanent misconfiguration. Report ready, so the\n      // teardown runs, throws, and counts toward the cap.\n      if (!recordedProvider) return true;\n      // A built-in provider has no plugin worker, so it is always ready.\n      if (isBuiltinSandboxProvider(recordedProvider)) return true;\n      // No worker manager is a permanent condition here. Report ready, so the\n      // teardown runs, throws its own \"no worker manager\" error, and counts\n      // toward the cap.\n      if (!pluginWorkerManager) return true;\n      // Resolve the installed plugin without a wait. A plugin reload or a plugin\n      // reinstall can remove the plugin row for a short window, so a missing\n      // plugin is a transient condition, not a permanent one. Report not ready,\n      // so the sweep skips the lease without a claim, and a later sweep retries\n      // after the plugin returns. A teardown while the plugin is missing only\n      // throws and burns a finite attempt, so a long reload could exhaust the\n      // retries and strand the sandbox. A permanent uninstall also cannot tear\n      // the sandbox down, because there is no worker to call, so the preserved\n      // pending_cleanup row still holds the durable cleanup state for later.\n      const installed = await resolvePluginSandboxProviderDriverByKey({\n        db,\n        driverKey: recordedProvider,\n        workerManager: pluginWorkerManager,","sourceCodeStart":2525,"sourceCodeEnd":2561,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/server/src/services/environment-runtime.ts#L2525-L2561","documentation":"Thrown by the plugin sandbox driver's openDuplexChannel when pluginWorkerManager.getWorker(pluginId) returns null — the lease names a plugin whose worker process is not currently running on the host. The duplex route must bind to the same worker session the sandbox runner streams through, so a stopped/crashed/never-started worker is a hard failure for the channel open.","triggerScenarios":"openDuplexChannel with valid lease metadata while the plugin's worker process is down: the plugin was disabled or failed to boot, the worker crashed earlier and was not restarted, or the channel open races worker startup right after server boot.","commonSituations":"Plugin worker crashed (OOM, unhandled error) leaving leases valid but workers dead; server restarted and lazy worker start has not happened yet for that plugin; plugin uninstalled/updated between lease creation and channel open.","solutions":["Check plugin worker status on the server (plugin registry/manager logs) for the named pluginId.","Restart or re-enable the plugin so its worker boots, then retry the channel open.","If the worker keeps dying, inspect the plugin worker logs for the crash cause and fix the plugin before retrying.","If the lease outlived a plugin upgrade, acquire a fresh sandbox lease from the current plugin version."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"const worker = pluginWorkerManager.getWorker(pluginId);\nif (!worker) { await pluginWorkerManager.ensureWorkerStarted(pluginId); } // start the worker before opening the channel","typeGuard":null,"tryCatchPattern":"try { return await driver.openDuplexChannel(input); } catch (err) { if (/is not running for the duplex channel/.test((err as Error).message)) { await restartPluginWorker(pluginId); return await driver.openDuplexChannel(input); } throw err; }","preventionTips":["Ensure plugin workers are started (or eagerly warmed) before agents begin duplex sessions.","Monitor plugin worker health and auto-restart crashed workers.","Acquire fresh leases after plugin upgrades rather than reusing leases bound to a dead worker session."],"tags":["sandbox","duplex-channel","plugin-worker","not-running","retry"],"backgroundTag":"worker-not-running","analyzedSha":"3f1d897a7c018d76563a21c6e39c3c9b03933622","analyzedAt":"2026-08-21T17:58:32.592Z","contentChangedAt":"2026-08-21T17:58:32.592Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}