paperclipai/paperclip · error

LOGIN_PTY_OPEN_FAILED

LOGIN_PTY_OPEN_FAILED

Error message

LOGIN_PTY_OPEN_FAILED

What it means

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.

Source

Thrown at server/src/services/plugin-worker-manager.ts:1811

          providerLeaseId: input.providerLeaseId,
          loginCommandKey: input.loginCommandKey,
          sessionHome: input.sessionHome,
        },
        loginPtyOpenTimeoutMs,
      );
    } catch (err) {
      // A send failure, an RPC rejection, or an open timeout. Terminalize the
      // route exactly once and fail closed.
      await terminalizeLoginPtyRoute(route);
      throw err instanceof Error ? err : new Error(LOGIN_PTY_OPEN_FAILED);
    }

    const workerSessionId = readBindableWorkerSessionId(route, openResult);
    if (!workerSessionId) {
      // A malformed reply, or a route that already left `opening`. A late or a
      // duplicate reply never binds, revives, or reopens a route.
      await terminalizeLoginPtyRoute(route);
      throw new Error(LOGIN_PTY_OPEN_FAILED);
    }
    if (loginPtyRoutesByWorkerSessionId.has(workerSessionId)) {
      // A live route already owns this worker session identifier. Fail closed
      // instead of binding a second route to it.
      await terminalizeLoginPtyRoute(route);
      throw new Error(LOGIN_PTY_OPEN_FAILED);
    }
    // Bind the worker session identifier one time and move the route to `open`.
    route.workerSessionId = workerSessionId;
    route.state = "open";
    loginPtyRoutesByWorkerSessionId.set(workerSessionId, route);
    // Replay every record the route queued before the bind, in arrival order.
    replayPreBindLoginPtyRecords(route);

    return {
      onData(listener: (chunk: string) => void): void {
        route.listener = listener;
        if (route.buffered.length > 0) {

View on GitHub (pinned to 01ad858492)

Solutions

  1. Inspect worker logs for malformed open replies or crashes during PTY open
  2. Retry opening the login PTY route once; state machine resets via terminalizeLoginPtyRoute
  3. Update/upgrade the plugin worker so open replies always carry a bindable session id
  4. Serialize close/terminalize against open replies to avoid the route leaving `opening` before the reply is processed
Defensive patterns

Strategy: retry

Try / catch

try {
  const handle = await createPluginWorkerHandle({ kind: 'login-pty', route });
} catch (e) {
  if ((e as Error).message === 'LOGIN_PTY_OPEN_FAILED') {
    // route was terminalized; safe to retry the open once
  } else throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10). Data as JSON: /api/errors/c2b7e7515a5ca1b9. Report an issue: GitHub.