Skip to content

fix: MKT-19619 appSdk.api() drops body/status text/headers on 4xx/5xx - #190

Open
amit-kanswal-cs wants to merge 3 commits into
developfrom
fix/MKT-19619-api-error-response
Open

amit-kanswal-cs wants to merge 3 commits into
developfrom
fix/MKT-19619-api-error-response

Conversation

@amit-kanswal-cs

@amit-kanswal-cs amit-kanswal-cs commented Oct 23, 2026 •

Copy link
Copy Markdown

Summary

Fixes MKT-19619: appSdk.api() returned body: null, statusText: "Internal Server Error" and the request headers for every 4xx/5xx proxied through the platform, so apps could see that a call failed but never why.

Root cause

The host (app-extension-component events.tsx (api)) catches the axios error and resolves the apiAdapter reply with err.response. dispatchAdapter then rejected { ...response, config }, a flat object whose keys are data / status / statusText / headers / config / request with no response wrapper. dispatchApiRequest's if (err.response) branch therefore never ran, and the transport-error fallback built the Response from err.stack / err.message / err.config.headers, none of which describe the HTTP reply.

Separately, axiosToFetchResponse (success path) read config.headers, i.e. the request headers, so apps could not read response headers from api() at all. That regressed in 5a8bd80 ("refactor headers logic").

Changes

  • dispatchAdapter — for HTTP errors (reply has a data key) also nest the reply under response. The flat keys are kept, so anything reading err.status / err.data off a createAdapter() rejection keeps working. Host transport errors ({ status: 502|503|504, message, stack }) are rejected unchanged.
  • dispatchApiRequest — convert err.response with axiosToFetchResponse (object bodies become JSON, not [object Object]); err.config?.headers on the fallback.
  • axiosToFetchResponse — build headers from the response headers; strip set-cookie and authorization.

No host change needed. createAdapter() behaviour is additive only (one extra response key on HTTP-error rejections).

Tests

New __test__/adapter.test.ts drives dispatchApiRequest and dispatchAdapter through a mocked post-robot:

Case develop this PR
200 JSON, response header passes through, request header does not ❌ ✅
404 JSON object body (ticket repro) ❌ ✅
404 string body ❌ ✅
500 text/plain body ❌ ✅
host 502 transport shape keeps its message ✅ ✅
post-robot rejection → 500 ✅ ✅
set-cookie / authorization stripped ❌ ✅
createAdapter resolves on 200 ✅ ✅
createAdapter rejects 404 with flat keys + nested response ❌ ✅
createAdapter rejects transport error without response ✅ ✅
createAdapter rejects AxiosError on post-robot failure ✅ ✅
axiosToFetchResponse ×3 ❌ ✅

Jest 27 exposes no fetch globals in either environment, so __test__/helpers/fetchEnvironment.js copies Node's own Response / Headers / Blob into the sandbox for this file.

Full suite: 19 suites, 263 tests pass. tsc --noEmit and webpack build clean. ESLint: 0 errors in touched files (the 8 pre-existing warnings are in src/types*, untouched). Prettier: new files formatted; adapter.ts was already non-conformant on develop at the dispatchAdapter signature, left as-is to keep the diff focused.

CI fixes (second commit)

Both security-sca and security-license were already red on develop; fixed here so the PR can merge.

  • security-sca — Snyk flagged 37 issues, all via axios@1.12.2 (lock) and its transitive form-data@4.0.4 / follow-redirects@1.15.9. Bumped axios ^1.7.9 → ^1.20.0 (the version Snyk recommends), which pulls form-data@4.0.6 and follow-redirects@1.16.1. npm audit --omit=dev reports 0. Full suite, tsc and webpack still pass; bundle 314 → 329 KiB.
  • security-license — LICENSE copyright range 2022-2025 → 2022-2026; the policy scan greps for the current year.

Follow-ups (not in this PR)

  • Version bumped to 2.5.0 in this PR (last published: 2.4.1). Once released, marketplace-xtm-app can drop the createAdapter() workaround (PR #1061).
  • v3: have dispatchAdapter reject with a real AxiosError for parity with axios.
  • Product wiki: add gotchas for both behaviours and document the apiAdapter error contract.

🤖 Generated with Claude Code

…() errors

appSdk.api() returned `body: null`, `statusText: "Internal Server Error"`
and the *request* headers for every 4xx/5xx proxied through the platform.

The host resolves the `apiAdapter` channel with the upstream AxiosResponse
for HTTP errors; dispatchAdapter then rejected `{ ...response, config }`,
a flat object with no `response` key, so dispatchApiRequest's
`err.response` branch never ran and the transport-error fallback built the
Response from fields that do not exist on that object.

Changes
- dispatchAdapter: for HTTP errors also nest the reply under `response`.
  The flat keys (status, data, headers) are kept so createAdapter()
  consumers are unaffected. Transport errors ({ status, message, stack })
  are rejected unchanged.
- dispatchApiRequest: convert `err.response` with axiosToFetchResponse so
  object bodies are JSON-encoded instead of "[object Object]"; guard
  `err.config?.headers` on the fallback.
- axiosToFetchResponse: build headers from the *response* headers instead
  of the request config (apps could not read response headers even on
  success); drop set-cookie and authorization.
- Tests: new __test__/adapter.test.ts drives dispatchApiRequest and
  dispatchAdapter through a mocked post-robot (200, 404 JSON/string,
  500 text, host 502 transport shape, post-robot failure, header
  pass-through and stripping). Jest 27 has no fetch globals, so a tiny
  custom environment copies Node's Response/Headers/Blob into the sandbox.
  9 of the 14 new cases fail on develop and pass with this change.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@snyk-io

snyk-io Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

✅ Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
✅ Open Source Security 0 0 0 0 0 issues
✅ Licenses 0 0 0 0 0 issues
✅ Code Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

- Upgrade axios ^1.7.9 -> ^1.20.0 (lock 1.12.2 -> 1.20.0). Snyk flagged 37
  issues in axios@1.12.2 and its transitive form-data@4.0.4 /
  follow-redirects@1.15.9; 1.20.0 is the version Snyk recommends and pulls
  form-data 4.0.6 and follow-redirects 1.16.1. `npm audit --omit=dev`: 0.
- LICENSE: copyright range 2022-2025 -> 2022-2026 (policy-scan requires
  the current year).

Both checks were already failing on develop; no source change.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Minor bump: api() now returns the backend body, status text and response
headers on 4xx/5xx and exposes response headers on success; createAdapter()
rejections gain a nested `response` key (additive). Last published is 2.4.1.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants