apache/beam · warning

Control channel closed.

Error message

Control channel closed.

What it means

The Beam TypeScript worker warns 'Control channel closed.' when the gRPC control stream from the Fn API control service ends. The constructor then closes all data channels best-effort. This indicates the runner/controller closed or terminated the control connection, so the worker can no longer receive instructions.

Solutions

  1. If the pipeline completed normally, ignore — this is expected shutdown logging.
  2. If it appears mid-run, check connectivity/proxy keepalive settings between worker and control service and inspect runner logs for harness restarts.
  3. Rely on the runner's retry/restart of workers; ensure the control service address and credentials are correct.

Example fix

// before
worker without reconnect handling; channel end -> closed permanently
// after
controlChannel.on("end", () => {
  if (!shuttingDown) {
    console.error("Control channel closed unexpectedly; restarting worker");
    restart();
  }
});
Defensive patterns

Strategy: try-catch

Try / catch

controlChannel.on("end", () => {
  if (!shuttingDown) {
    console.error("Control channel closed unexpectedly");
    scheduleWorkerRestart();
  }
});
controlChannel.on("error", (err) => console.error("Control channel error:", err));

Prevention

When it happens

Trigger: The control gRPC server stream emits 'end' — e.g. the job finishes, the runner shuts down the harness, or the control connection drops unexpectedly.

Common situations: Normal pipeline shutdown (benign); network interruption or proxy killing long-lived gRPC streams; the controlling process crashing so the worker's channels end prematurely.

Related errors


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/d642d48fce80e3d6. Report an issue: GitHub.

Appendix: source

Thrown at sdks/typescript/src/apache_beam/worker/worker.ts:96

  metricsShortIdCache = new MetricsShortIdCache();

  constructor(
    private id: string,
    private endpoints: WorkerEndpoints,
    options: Object = {},
  ) {
    const metadata = new grpc.Metadata();
    metadata.add("worker_id", this.id);
    this.controlClient = new BeamFnControlClient(
      endpoints.controlUrl,
      grpc.ChannelCredentials.createInsecure(),
      {},
      {},
    );
    this.controlChannel = this.controlClient.control(metadata);
    this.controlChannel.on("data", this.handleRequest.bind(this));
    this.controlChannel.on("end", () => {
      console.warn("Control channel closed.");
      for (const dataChannel of this.dataChannels.values()) {
        try {
          // Best effort.
          dataChannel.close();
        } finally {
        }
      }
      for (const stateChannel of this.stateChannels.values()) {
        try {
          // Best effort.
          stateChannel.close();
        } finally {
        }
      }
    });
  }

  async wait() {

View on GitHub (pinned to 12126d8942)