# ADR-002: Request ID Propagation via X-Request-ID Header **Status:** Accepted **Date:** 2026-03-18 ## Context In a distributed system, a single inbound request may fan out to multiple downstream service calls. Without a shared correlation identifier, tracing a request across service logs requires matching timestamps or other heuristics. A request ID, propagated as an HTTP header (`X-Request-ID`), lets logs across services be correlated by a single value. The `logz` module owns the request ID context key (ADR-003 global: context helpers live with data owners). `httpclient` depends on `logz` and should use its helpers rather than define its own context key. ## Decision Inside the retry function in `Do`, before executing each request attempt, the client reads the request ID from the context using `logz.GetRequestID(req.Context())`. If a non-empty value is present, it is set as the `X-Request-ID` header on the outgoing request: ```go if id := logz.GetRequestID(req.Context()); id != "" { req.Header.Set("X-Request-ID", id) } ``` The header is set on every retry attempt, not just the first, because the same `*http.Request` object is reused across retries. If no request ID is present in the context (the ID is the zero string), the header is not set. This is verified by `TestClient_Do_NoRequestID`. ## Consequences **Positive:** - Request IDs flow automatically to downstream services without any caller boilerplate. - Correlation across service boundaries works with no additional middleware. - The integration is testable: `TestClient_Do_InjectsRequestID` verifies end-to-end propagation using `logz.WithRequestID` and an `httptest.Server`. **Negative:** - `httpclient` takes a direct import dependency on `logz`. This is accepted per ADR-001 (global) which permits direct imports between framework modules. - The header name `X-Request-ID` is hardcoded. Projects that use a different header name (e.g. `X-Correlation-ID`) cannot configure this without forking the client. - Header propagation only works when the caller places the request ID in the context via `logz.WithRequestID`. Requests built without a context carrying a request ID will not have the header set.