Skip to content

fix(core)!: keep literal request headers named after a method - #11156

Merged
jasonsaayman merged 8 commits into
axios:v1.xfrom
official-burak:fix/literal-method-named-headers
Oct 12, 2026
Merged

jasonsaayman merged 8 commits into
axios:v1.xfrom
official-burak:fix/literal-method-named-headers

Conversation

@official-burak

@official-burak official-burak commented Aug 19, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Request preparation removes common and method-named defaults after flattening them. Since #11096 extended cleanup to every supported method, literal headers such as Link have also been removed. Treating every object as a defaults bucket additionally drops Date, Buffer, typed-array, and application class values before normal header serialization.

Limit defaults buckets to plain/null-prototype maps and compatible AxiosHeaders, including subclasses and instances from another package build. Other values reach normal header serialization. Common defaults apply to every request; method defaults apply only to the matching method.

Breaking compatibility change: custom class instances used as defaults buckets must be converted to a plain map or AxiosHeaders. Otherwise they become literal header values, including on nonmatching requests.

Linked issue

No separate issue. Fixes the literal-header regression introduced by #11096 and addresses the non-plain object finding, throwing brand-probe finding, and tag-only misclassification finding.

Changes

  • Recognize plain header maps and local AxiosHeaders; require the AxiosHeaders symbol brand and the core header-container interface for foreign instances (toJSON, set, get, has, delete, clear, normalize, concat, toString, and iteration). A tag and toJSON alone do not classify an application object as a defaults bucket. Methods are checked for callability without invoking them, and getSafeProp excludes shared-prototype values.
  • Catch errors from the optional foreign-instance probes. Throwing brand or interface getters now fall back to literal-header serialization; genuine serialization errors still reject the request.
  • Cover scalar and non-plain values, matching/nonmatching methods, common defaults, precedence, subclasses, cross-build instances, shared-prototype pollution, own/inherited throwing getters, and tagged application literals. Packed ESM/CJS tests also cover foreign subclasses. Preserve nullish cleanup and false-valued omission.
  • Update the existing prerelease changelog entry and record the class-defaults migration in deferred release documentation.

Validation

Environment: macOS arm64, Node.js 24.18.0, npm 11.16.0. Base: v1.x at f694ecd6. Latest fix: d2a6e81f.

  • npm ci --ignore-scripts: passed in the root and both smoke fixtures.
  • Before the interface-validation fix, npm run test:vitest:unit -- tests/unit/axios.test.js reproduced both new tagged-literal failures while the existing 37 tests passed. The final focused run, including own/inherited interface-getter failures, the serialization-error negative case, and tests/unit/syntaxCompat.test.js, passed 113 tests (41 Axios + 72 syntax checks). The earlier throwing-brand regression coverage remains intact.
  • npm run lint and npm run build: passed.
  • npm run test:vitest:unit: 1,604 passed across 69 files.
  • npm run test:vitest:browser:headless: 858 passed, 3 skipped across Chromium, Firefox, and WebKit. Installed Playwright browsers were available.
  • npm pack --silent --pack-destination <temporary-directory>, then npm install --no-save --ignore-scripts --package-lock=false <tarball> in each smoke fixture: passed.
  • npm run test:smoke:esm:vitest in tests/smoke/esm: 78 passed, including ESM/CJS constructor and subclass interoperability.
  • npm run test:smoke:cjs:mocha in tests/smoke/cjs: 72 passed, including defaults and subclasses from another packaged build.
  • Prettier checks for the changed files and changed browser test block and git diff --check: passed. Final diff inspected; no dependency, workflow, generated-artifact, version, debug, or focused-test changes.

GitHub Actions for d2a6e81f5d0a7c3b49ee08087214ff5a6977706e require approval (action_required): Continuous integration, Bundle Size, zizmor, and build reproducibility. Their runtime/build results remain unverified on GitHub. The PR is conflict-free. Review bot results are separate from build/test evidence.

Compatibility and documentation

Semver: major compatibility impact for arbitrary class defaults. Plain/null-prototype maps and compatible AxiosHeaders retain defaults-bucket behavior. Other objects are literal header values. Applications using custom class instances as defaults maps must materialize their own enumerable header entries:

// Before
headers: { post: customDefaults }

// Migrate to either form
headers: { post: { ...customDefaults } }
headers: { post: new AxiosHeaders({ ...customDefaults }) }

Without migration, a custom toString() may expose object contents in a literal header, including on nonmatching requests. This intentional restriction must be accounted for when scheduling the release; the overall PR is not a universally nonbreaking patch. Foreign header containers retain support through structural interface validation; tag-only application objects remain literal values. Failed optional probes fall back to normal serialization.

No new APIs, options, exports, dependencies, or declaration changes. Object-to-string normalization is existing JavaScript behavior and does not expand TypeScript header-value declarations. PRE_RELEASE_CHANGELOG.md describes the final behavior; PRE_RELEASE_DOCS.md records deferred header/defaults guidance and migration. README and docs-site updates remain deferred to release preparation.

Type/module declaration tests are not applicable because declarations and exports are unchanged; both runtime package formats were tested from the tarball. The full GitHub runtime matrix, including Bun and Deno, was not reproduced locally.

Checklist

  • Scope, target branch, and linked issue/rationale checked
  • PRE_RELEASE_CHANGELOG.md entry added or updated
  • Regression tests and relevant negative/platform cases covered
  • Applicable local checks completed successfully; commands, results, and runtime versions recorded above
  • Deferred release docs and ESM/CJS types/tests updated where needed, or applicability explained
  • Semver impact stated and intentional breaking changes explained
  • Final diff reviewed for security, unrelated changes, generated artifacts, and test/debug leftovers
  • Latest GitHub Actions status recorded separately from review/security bot results

Request preparation deleted every `common` and method key from the merged
headers, so a literal header whose name matches a supported method was
dropped instead of being sent. `Link` is the notable case: it is a real
request header, and header names are lowercased during config merge, so
both `link` and `Link` were removed.

Treat a `common` or method key as a structural default bucket only when it
holds an object, and strip it in that case only. Literal values under those
names now reach the adapter, while method-specific header defaults keep
being removed as before.
@greptile-apps

greptile-apps Bot commented Aug 19, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 5/5

[High impact] No blocking failure remains.

T-Rex evidence

Evidence from the check

  • This authored executable builds both method-named header objects and observes final headers in a deliberately failing in-process Axios request, with the takeaway that the full interface changes flattening behavior.

Command output from the check

  • This captured command output shows the request reached the rejecting adapter and retained the literal common header when the object lacks one required callable member, establishing the control behavior.

Command output from the check

  • This captured command output shows the request reached the rejecting adapter but no longer emitted the literal common header after the full spoofed interface was supplied, confirming the claim.

View artifacts

Summary

This update refines Axios request-header preparation so supported header-default containers are flattened consistently while ordinary header values retain their intended request semantics.

Reviews (7) · Last reviewed commit: "fix(core): validate foreign header bucke..." · Reviewed by Greptile

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 3 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread lib/core/Axios.js Resolved
…eaders

Any non-null object under a method name was treated as a defaults bucket,
so a literal header holding a Date, Buffer or class instance was silently
dropped. Restrict the check to plain objects and AxiosHeaders instances,
which are the only shapes a defaults bucket can take.
@official-burak

official-burak commented Aug 19, 2026 •

Copy link
Copy Markdown
Contributor Author

Good catch on utils.isObject. Pushed a fix in 4073160.

I did not take the suggested isPlainObject swap verbatim, because on its own it regresses a shape that currently works: an AxiosHeaders instance assigned to headers.common or headers[method]. isPlainObject returns false for it, so the bucket would stop being merged and would instead be sent as a literal header named common. utils.merge handles an AxiosHeaders instance correctly today, so that would be a real loss.

The check is now:

const isHeadersBucket = (value) =>
  value == null || utils.isPlainObject(value) || value instanceof AxiosHeaders;

This covers both cases: Date, Buffer and other class instances are no longer swallowed as buckets, while plain objects, null-prototype objects and AxiosHeaders still are. The explicit isArray guard is gone because isPlainObject already rejects arrays.

Verified against all three variants with two added tests:

isHeadersBucket implementation Result
isObject (previous) fails "non-plain object as literal header"
isPlainObject only fails "AxiosHeaders instance as defaults"
isPlainObject || instanceof AxiosHeaders 10/10 pass

Full unit suite is green: 58 files, 1065 tests.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 2 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread tests/unit/axios.test.js Resolved
The three header tests added by this change each declared an identical
inline `echoHeaders` adapter. `tests/README.md` asks for reusable
helpers to live in `tests/setup` rather than being copied between
tests, so move it to `tests/setup/adapters.js` and import it.

Test-only change; no production code is touched.
@jasonsaayman jasonsaayman added commit::fix The PR is related to a bugfix v1x Version 1 labels Sep 1, 2026
@jasonsaayman jasonsaayman added the ai-assisted This PR was created with or in conjunction with AI label Oct 9, 2026
@jasonsaayman
jasonsaayman marked this pull request as draft October 9, 2026 17:38
@jasonsaayman
jasonsaayman marked this pull request as ready for review October 9, 2026 17:40

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 8 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

View guided diff | Re-trigger cubic

Comment thread lib/core/Axios.js Resolved
Recognize plain header maps and compatible AxiosHeaders across builds while allowing Date, Buffer, typed-array, and application class values to reach normal header serialization.

BREAKING CHANGE: Custom class instances used as common or method defaults must be converted to plain maps or AxiosHeaders. Other non-plain objects are now literal header values.
@jasonsaayman jasonsaayman changed the title fix(core): keep literal request headers named after a method fix(core)!: keep literal request headers named after a method Oct 9, 2026

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 7 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

View guided diff | Re-trigger cubic

Comment thread lib/core/Axios.js Resolved
Comment thread lib/core/Axios.js Resolved
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-assisted This PR was created with or in conjunction with AI commit::fix The PR is related to a bugfix v1x Version 1

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants