grafana/k6 · error

connection closed with websocket code: %d

Error message

connection closed with websocket code: %d

What it means

Connection.close records the websocket close code as the cancel cause for the connection context, so every pending or future operation on that connection fails with this message via ContextErr. The %d is the RFC 6455 close code: 1000/1001 are orderly shutdowns, 1006 means an abnormal drop (no close frame), others indicate protocol-level failures.

Source

Thrown at internal/js/modules/k6/browser/common/connection.go:201

		errorCh:                  make(chan error),
		done:                     make(chan struct{}),
		closing:                  make(chan struct{}),
		msgIDGen:                 &msgID{},
		sessions:                 make(map[target.SessionID]*Session),
		onTargetAttachedToTarget: onTargetAttachedToTarget,
	}

	go c.recvLoop()
	go c.sendLoop()

	return &c, nil
}

func (c *Connection) close(code int) error {
	c.logger.Debugf("Connection:close", "code:%d", code)

	defer func() {
		c.cancelCtx(fmt.Errorf("connection closed with websocket code: %d", code))
	}()

	var err error
	c.shutdownOnce.Do(func() {
		defer func() {
			// Stop the main control loop
			close(c.done)
			_ = c.conn.Close()
		}()

		c.closeAllSessions()

		err = c.conn.WriteControl(websocket.CloseMessage,
			websocket.FormatCloseMessage(code, ""),
			time.Now().Add(time.Second),
		)

		// According to the WS RFC[1], we might want to wait for a response

View on GitHub (pinned to 93accf6570)

Solutions

  1. Map the code: 1000/1001 = expected shutdown (usually harmless if during teardown); 1006 = crash/network drop worth investigating.
  2. Await all pending operations (await page.goto(...)) before closing pages/browser so nothing races the close.
  3. For 1006, check the browser process lifetime and memory limits — it was killed or dropped.
  4. Move cleanup into the iteration teardown and guard it so a normal close cannot interrupt in-flight work.

Example fix

// before
page.goto(url);          // not awaited
await browser.close();   // races the navigation

// after
await page.goto(url);    // fully awaited
await browser.close();
Defensive patterns

Strategy: try-catch

Try / catch

try {
  await page.evaluate('1+1');
} catch (e) {
  const m = String(e).match(/connection closed with websocket code: (\d+)/);
  if (m) {
    const code = Number(m[1]);
    if (code === 1000 || code === 1001) {
      // orderly shutdown (test teardown) — ignore
    } else {
      // abnormal close (e.g. 1006): browser crashed or connection dropped — investigate
      console.error('abnormal websocket close', code);
    }
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: Browser (or target) closes the websocket during a CDP call: browser.Close(), page/target destroyed, browser crash (1006), or k6 itself closing the connection after receiving a close frame — the code then propagates to all in-flight sends/waits.

Common situations: Test tears down while a navigation or evaluation is still pending; chromium crashes mid-run; remote browser restarted under k6; the code appearing repeatedly in logs usually means an operation raced the close instead of being awaited first.

Understand the failure class

Related errors


AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15). Data as JSON: /api/errors/00c7d1bf10e33547. Report an issue: GitHub.