github/copilot-sdk · error

WebSocket response bridge is not attached

Error message

WebSocket response bridge is not attached

What it means

CopilotRequestHandler's protected constructor requires a WebSocket response bridge to have been attached to the CopilotRequestContext under the internal kBridge symbol. The bridge is what carries responses back to the runtime over the WebSocket RPC channel. If it is missing, the handler cannot emit responses, so construction fails immediately.

Solutions

  1. Do not construct request handlers yourself; let the Copilot runtime create them and only implement the handler entry points.
  2. If subclassing, pass through the exact context object given to your handler factory/callback, never a hand-built context object.
  3. In tests, use the SDK's provided test harness/factory that attaches the internal bridge rather than constructing the handler directly.
  4. Align SDK versions so the runtime attaching the bridge and the handler reading kBridge are the same package version.

Example fix

// before
const ctx = { requestId: 'r1' } as CopilotRequestContext;
const handler = new MyHandler(ctx); // throws: no bridge
// after
export default createRequestHandler((ctx) => new MyHandler(ctx)); // runtime-attached context
Defensive patterns

Strategy: validation

Validate before calling

function canCreateHandler(ctx: unknown): ctx is CopilotRequestContext {
  return !!ctx && typeof ctx === 'object' && !!(ctx as Partial<InternalContext>)[kBridge];
}
if (!canCreateHandler(ctx)) throw new Error('context lacks WebSocket response bridge');

Type guard

const isAttachableContext = (c: unknown): c is CopilotRequestContext & { [kBridge]?: unknown } =>
  typeof c === 'object' && c !== null;

Try / catch

try {
  const handler = new MyHandler(ctx);
} catch (e) {
  if (e instanceof Error && e.message.includes('bridge is not attached')) {
    // context not runtime-provided; fix construction site
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling the protected CopilotRequestHandler constructor (directly or from a subclass via super(context)) with a context object created outside the runtime's request-dispatch path, so no bridge was attached under kBridge.

Common situations: Subclassing CopilotRequestHandler and instantiating it manually in tests or scripts with a plain/partial context instead of receiving the runtime-provided context; using an older/newer SDK version where the internal bridge key or attachment mechanism changed.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09). Data as JSON: /api/errors/bc4d087647779c90. Report an issue: GitHub.

Appendix: source

Thrown at nodejs/src/copilotRequestHandler.ts:85

 * forwarding traffic to the real upstream, subclass {@link CopilotWebSocketForwarder}
 * instead, which connects upstream and forwards by default.
 *
 * @experimental
 */
export abstract class CopilotWebSocketHandler implements AsyncDisposable {
    readonly #response: CopilotWebSocketResponseBridge;
    readonly #completion: Promise<CopilotWebSocketCloseStatus>;
    #resolveCompletion!: (status: CopilotWebSocketCloseStatus) => void;
    #closed = false;
    [kSuppressCloseOnDispose] = false;

    protected readonly context: CopilotRequestContext;

    protected constructor(context: CopilotRequestContext) {
        this.context = context;
        const bridge = (context as Partial<InternalContext>)[kBridge];
        if (!bridge) {
            throw new Error("WebSocket response bridge is not attached");
        }
        this.#response = bridge;
        this.#completion = new Promise<CopilotWebSocketCloseStatus>((resolve) => {
            this.#resolveCompletion = resolve;
        });
    }

    async sendResponseMessage(data: string | Uint8Array): Promise<void> {
        await this.#response.write(data);
    }

    async close(
        status: CopilotWebSocketCloseStatus = CopilotWebSocketCloseStatus.normalClosure
    ): Promise<void> {
        if (this.#closed) {
            return;
        }
        this.#closed = true;

View on GitHub (pinned to cd8cf15dc3)