Skip to content

axios response.headers is an empty object (status and data correct); node:http's own res.headers works, so the gap is in axios's header assembly #10744

Description

@proggeramlug

response.headers is an empty object on an axios response under Perry, where Node populates it. Status codes and response bodies are correct; only the headers object is unpopulated.

Found while validating PR #10679 (remove the axios native binding). It is pre-existing and not caused by that PR — see the attribution below — so #10679 is not blocked by it.

Observed

Real npm axios compiled from source (perry.compilePackages wildcard, 27-package transitive graph, 130 native modules), GET and POST against a local server:

Perry Node 26.5.1
res.status correct correct
res.data correct correct
Object.keys(res.headers) [] 5 keys
res.headers["content-type"] undefined "application/json"

Not a casing problem: res.headers["Content-Type"] is also undefined, and the object is genuinely empty rather than differently keyed.

Attribution — four steps, in increasing cost

1. Dependency-free node:http control, no axios anywhere. Does not diverge: content-type reads application/json, correctly cased and a string, on both Perry and Node. The only difference is content-length vs transfer-encoding framing, which is unrelated. So this is not a generic client-transport gapnode:http's own res.headers works.

2. Absent, not miscased or mistyped. Object.keys() is empty, the value is undefined, typeof is "undefined". The three possibilities have different owners and this is the first.

3. Not #10668's documented limitation. That PR records a known gap in rawHeaders' name casing on the array form, and explicitly states that res.headers — the lower-cased-key object used here — is correct. Checked by reading the PR body rather than assuming from the title.

4. Worse on pristine main with the native binding in place. Verified the native binding was actually the one serving the import (compile log: serving axios from the bundled native binding perry-ext-axios... ignoring your installed node_modules/axios, Found 1 module(s): 1 native). Result there:

GET status: undefined      GET data: undefined
POST status: undefined     POST data: undefined
crash: Cannot read properties of undefined (reading 'content-type')

The entire response object is broken under the native binding, not merely its headers.

Consequence

Real-source axios is strictly better than the native binding on this path — it gets status and data right and leaves only headers incomplete, where the native binding gets nothing right and then crashes. That is a point in favour of the removal, not against it.

The remaining gap is axios's own header-population logic not working under Perry, independent of which axios implementation is used. Since node:http's res.headers is correct (step 1), the defect is in how axios reads or assembles headers from the response — likely its AxiosHeaders construction from the underlying response — rather than in the client transport itself. That is the place to look first.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    package-auditFound by the 2026 package audit: compiling real npm packages from source instead of native bindings

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions