juspay/hyperswitch · error

a captured response must reconstruct

Error message

a captured response must reconstruct

What it means

After capturing a response via response_result, the test calls `replay_response(&captured).expect("a captured response must reconstruct")`; if the captured boundary representation cannot be rebuilt into a reqwest::Response, the expect panics. It asserts the capture/reconstruct round-trip invariant of the HTTP boundary layer.

Solutions

  1. Inspect why replay_response returns Err (likely header reconstruction) and fix the capture/replay codec
  2. Ensure repeated set-cookie headers are stored as a list and re-attached with HeaderValue::append on replay
  3. Add a debug print of the captured struct when the expect fires

Example fix

// before
let reconstructed = replay_response(&captured).expect("a captured response must reconstruct");
// after
let reconstructed = replay_response(&captured)
    .unwrap_or_else(|e| panic!("a captured response must reconstruct: {e:?}"));
Defensive patterns

Strategy: try-catch

Validate before calling

if captured.status.is_none() || captured.headers.is_empty() { panic!("capture looks empty before replay"); }

Type guard

fn can_replay(c: &CapturedResponse) -> bool { c.status.is_some() && !c.body.is_empty() }

Try / catch

replay_response(&captured).unwrap_or_else(|e| panic!("a captured response must reconstruct: {e:?}"))

Prevention

When it happens

Trigger: A regression in the capture or replay logic (e.g. repeated `set-cookie` headers dropped or malformed during serialization) makes replay_response return Err.

Common situations: Changes to the CustomResponse/CapturedResponse serialization that break multi-valued headers or body reconstruction in the http_client boundary module.

Related errors


AI-assisted analysis of juspay/hyperswitch@afd9284352 (2026-09-18). Data as JSON: /api/errors/f3fbd7bee4a3722d. Report an issue: GitHub.

Appendix: source

Thrown at crates/external_services/src/http_client/boundary.rs:274

            ("x-request-id", "req-1"),
            ("set-cookie", "a=1"),
            ("set-cookie", "b=2"),
            ("set-cookie", "c=3"),
        ] {
            builder = builder.header(name, value);
        }
        let source = reqwest::Response::from(
            builder
                .body(bytes::Bytes::from_static(b"{}"))
                .expect("failed to build the test response"),
        );

        let result: CustomResult<reqwest::Response, HttpClientError> = Ok(source);
        let (captured, _) = response_result(&result);
        let captured = captured.expose();

        let reconstructed =
            replay_response(&captured).expect("a captured response must reconstruct");
        let cookies: Vec<&str> = reconstructed
            .headers()
            .get_all("set-cookie")
            .iter()
            .filter_map(|value| value.to_str().ok())
            .collect();
        assert_eq!(
            cookies,
            ["a=1", "b=2", "c=3"],
            "every set-cookie value must survive the round trip"
        );
    }

    /// A recording written before the capture kept repeats stored one string per
    /// name, having already lost every repeated value. Reconstructing it would
    /// replay a tape that still carries the defect this fixes, so it refuses —
    /// and deja turns that refusal into a fail-stop with a named reason rather
    /// than a live call.

View on GitHub (pinned to afd9284352)