Skip to content

Bug: Test harness crashes with ERR_HTTP_HEADERS_SENT when no cached response found in CI #344

@brunoborges

Description

@brunoborges

Description

When running tests in CI mode (CI=true), the test harness proxy crashes with ERR_HTTP_HEADERS_SENT if a test triggers a request that has no cached response in the snapshot file.

This causes cascading test failures because the proxy process dies and subsequent tests cannot communicate with it.

Root Cause

In test/harness/replayingCapiProxy.ts around line 288-296, there is a missing return statement after calling exitWithNoMatchingRequestError():

// Fallback to normal proxying if no cached response found
// This implicitly captures the new exchange too
if (process.env.CI === "true") {
  await exitWithNoMatchingRequestError(
    options,
    state.testInfo,
    state.workDir,
    state.toolResultNormalizers,
  );
}
super.performRequest(options);  // <-- This still executes after exitWithNoMatchingRequestError!

The issue is that exitWithNoMatchingRequestError() calls options.onError() which writes HTTP headers and ends the response. But execution continues to super.performRequest(options), which also attempts to write HTTP headers, causing:

Error [ERR_HTTP_HEADERS_SENT]: Cannot write headers after they are sent to the client

This crashes the Node.js process entirely.

Reproduction

  1. Create a test that expects a timeout (like sendAndWait throws on timeout)
  2. The snapshot file intentionally has no assistant response
  3. Run the test in CI mode: CI=true npm test
  4. The proxy crashes, and any tests running after this one fail

Suggested Fix

Add return; after the exitWithNoMatchingRequestError() call:

if (process.env.CI === "true") {
  await exitWithNoMatchingRequestError(
    options,
    state.testInfo,
    state.workDir,
    state.toolResultNormalizers,
  );
  return;  // <-- Add this line
}
super.performRequest(options);

Impact

This bug is why the sendAndWait throws on timeout test is currently skipped in CI (it.skipIf(process.env.CI === "true")). With this fix, that test could potentially be enabled in CI.

Workaround

In the Java SDK implementation, we worked around this by adding an HTTP health check to detect when the proxy has crashed and automatically restart it before the next test runs. However, fixing the root cause in the harness would be cleaner.

Environment

  • Node.js: v22+
  • Discovered while implementing the Java SDK for copilot-sdk

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions