{"record":{"id":"c2b7e7515a5ca1b9","repo":"paperclipai/paperclip","slug":"login-pty-open-failed","errorCode":"LOGIN_PTY_OPEN_FAILED","errorMessage":"LOGIN_PTY_OPEN_FAILED","messagePattern":"LOGIN_PTY_OPEN_FAILED","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/src/services/plugin-worker-manager.ts","lineNumber":1811,"sourceCode":"          providerLeaseId: input.providerLeaseId,\n          loginCommandKey: input.loginCommandKey,\n          sessionHome: input.sessionHome,\n        },\n        loginPtyOpenTimeoutMs,\n      );\n    } catch (err) {\n      // A send failure, an RPC rejection, or an open timeout. Terminalize the\n      // route exactly once and fail closed.\n      await terminalizeLoginPtyRoute(route);\n      throw err instanceof Error ? err : new Error(LOGIN_PTY_OPEN_FAILED);\n    }\n\n    const workerSessionId = readBindableWorkerSessionId(route, openResult);\n    if (!workerSessionId) {\n      // A malformed reply, or a route that already left `opening`. A late or a\n      // duplicate reply never binds, revives, or reopens a route.\n      await terminalizeLoginPtyRoute(route);\n      throw new Error(LOGIN_PTY_OPEN_FAILED);\n    }\n    if (loginPtyRoutesByWorkerSessionId.has(workerSessionId)) {\n      // A live route already owns this worker session identifier. Fail closed\n      // instead of binding a second route to it.\n      await terminalizeLoginPtyRoute(route);\n      throw new Error(LOGIN_PTY_OPEN_FAILED);\n    }\n    // Bind the worker session identifier one time and move the route to `open`.\n    route.workerSessionId = workerSessionId;\n    route.state = \"open\";\n    loginPtyRoutesByWorkerSessionId.set(workerSessionId, route);\n    // Replay every record the route queued before the bind, in arrival order.\n    replayPreBindLoginPtyRecords(route);\n\n    return {\n      onData(listener: (chunk: string) => void): void {\n        route.listener = listener;\n        if (route.buffered.length > 0) {","sourceCodeStart":1793,"sourceCodeEnd":1829,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/server/src/services/plugin-worker-manager.ts#L1793-L1829","documentation":"After requesting a login PTY open, the manager derives a bindable worker session id from the reply. If the reply is malformed or the route already left the `opening` state (a late/duplicate open reply), the route is terminalized and Error('LOGIN_PTY_OPEN_FAILED') is thrown — a late or duplicate reply never binds, revives, or reopens a route.","triggerScenarios":"Worker returns an open reply lacking a usable session id; the open result references a route whose state is no longer `opening`; a duplicate open reply arrives after a prior one already advanced the state machine.","commonSituations":"Plugin worker crashed mid-open and sent a truncated reply; race between a close/terminalize request and the in-flight open reply; buggy worker adapter emitting a nonconforming open response; timeouts on the worker side.","solutions":["Inspect worker logs for malformed open replies or crashes during PTY open","Retry opening the login PTY route once; state machine resets via terminalizeLoginPtyRoute","Update/upgrade the plugin worker so open replies always carry a bindable session id","Serialize close/terminalize against open replies to avoid the route leaving `opening` before the reply is processed"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  const handle = await createPluginWorkerHandle({ kind: 'login-pty', route });\n} catch (e) {\n  if ((e as Error).message === 'LOGIN_PTY_OPEN_FAILED') {\n    // route was terminalized; safe to retry the open once\n  } else throw e;\n}","preventionTips":["Keep worker adapters conformant: open replies must always carry a bindable session id","Avoid closing/terminalizing a route while its open reply is in flight","Add integration tests for duplicate and late open replies","Update workers before changing the open-reply protocol"],"tags":["pty","worker-protocol","state-machine"],"backgroundTag":"unexpected-response-shape","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"}