paperclipai/paperclip · error

Worker for plugin "${pluginId}" is already ${status}

Error message

Worker for plugin "${pluginId}" is already ${status}

What it means

startInternal was called while the plugin worker is already in 'running' or 'starting' status. The state guard prevents double-start races from spawning duplicate worker processes for the same plugin.

Source

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

      // The worker returned a pair that is already live or already tombstoned. Fail
      // closed: terminalize this route and retire the worker before any reuse.
      await terminalizeDuplexChannelRoute(route);
      retireDuplexWorkerOnViolation("duplicate bound pair");
      throw new Error(DUPLEX_CHANNEL_OPEN_FAILED);
    }
    // Bind the worker session identifier one time and move the route from the
    // opening map to the live map under the exact pair key.
    route.workerSessionId = workerSessionId;
    route.state = "open";
    openingDuplexRoutes.delete(hostRouteId);
    liveDuplexRoutes.set(pairKey, route);

    // Start the route lifetime timer now the route is open. The route ends when
    // the timer expires. Every terminal path and the worker-exit path clears the
    // timer. Unreference the timer so it never blocks the host process shutdown.
    route.lifetimeTimer = setTimeout(() => {
      void terminalizeDuplexChannelRoute(route);
    }, maxDuplexChannelDurationMs);
    route.lifetimeTimer.unref?.();

    // Replay any frame that arrived before the bind. The route is live now, so the
    // exact-pair routing delivers each held frame or fails closed on a mismatch.
    replayPreBindDuplexFrames(route);

    // Send one host→worker request under the pending-request bound. End the route
    // when too many requests are in-flight, so a worker that never replies cannot
    // make the host hold an unbounded number of pending requests.
    const sendBoundedRequest = <
      M extends "duplexChannelWrite" | "duplexChannelStop",
    >(
      method: M,
      params: HostToWorkerMethods[M][0],
      // The exact raw byte count of a `duplexChannelWrite` payload, before base64
      // encoding. The caller supplies it: `params.data` on the wire is the base64
      // form (`ChannelBytesWireValue`), so its string length no longer equals the
      // real payload bytes. A stop request carries no payload and omits this.

View on GitHub (pinned to 01ad858492)

Solutions

  1. The worker is already in the requested state; check current status before issuing start/stop transitions and skip redundant transitions.
Defensive patterns

Strategy: type-guard

When it happens

Trigger: Thrown at server/src/services/plugin-worker-manager.ts:1717 when the library encounters an invalid state.

Common situations: See trigger scenarios.


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