Skip to content

[coverage] Conformance findings: CLOUDFETCH-017 (#471) - #567

Open
peco-engineer-bot[bot] wants to merge 12 commits into
mainfrom
ai/issue-471
Open

peco-engineer-bot[bot] wants to merge 12 commits into
mainfrom
ai/issue-471

Conversation

@peco-engineer-bot

Copy link
Copy Markdown
Contributor

Summary

Automated fix for #471 — [coverage] Conformance findings: CLOUDFETCH-017.

CloudFetchResultHandler.fetch() now catches the RetryError that HttpRetryPolicy throws when retries run out. It throws it again as a RetryError with the same errorCode and payload, so instanceof and errorCode checks still work, but with a fuller message: "CloudFetch result download failed: Max retry count exceeded (last error: HTTP 500: Internal Server Error)". If the last failure was a network error, the message ends with that error's message and code instead. Checked with the new e2e test against the live warehouse (a local server returning 500 for the CloudFetch links), which now passes, plus the two new unit tests. The full unit suite (1487 tests), lint and prettier all pass. npm run type-check fails, but only in examples/tokenFederation, which can't find '@databricks/sql'; that is not caused by this change.

Root cause & plan

Root cause: In CloudFetchResultHandler.fetch(), the download goes through HttpRetryPolicy.invokeWithRetry(). A persistent HTTP 500 counts as retryable (5xx other than 501), so once retryMaxAttempts runs out, computeRetry() throws new RetryError(RetryErrorCode.AttemptsExceeded, details). Its message is the fixed string 'Max retry count exceeded'. The HTTP response is only kept in payload. CloudFetchResultHandler doesn't catch this error, so it goes straight up through fetchNext/fetchChunk. The CloudFetch HTTP error ${status} ${statusText} branch in downloadLink() only runs when the retry policy returns a non-OK response without throwing, and that never happens for retryable statuses. As a result the caller gets an error that names neither the CloudFetch chunk download nor the HTTP status. TimeoutExceeded has the same problem, and so does a network error payload when retries run out.
Files: lib/result/CloudFetchResultHandler.ts, tests/e2e/cloudfetch.test.ts, tests/unit/result/CloudFetchResultHandler.test.ts
Planned coverage:

  • New test in tests/e2e/cloudfetch.test.ts. Open a session with small retry settings (e.g. retryMaxAttempts: 2, retryDelayMin: 1, retryDelayMax: 10, cloudFetchConcurrentDownloads: 1) and run the existing large range() query with useCloudFetch: true. Wait for operation.finished(), then get the CloudFetchResultHandler via getResultHandler().source.source. Start a local http.createServer that always replies 500 'Internal Server Error'. Wrap cfResultHandler.source.fetchNext so each returned resultLink's fileLink points at the local server, keeping the real expiryTime and rowCount from the warehouse. Call operation.fetchChunk({ maxRows: 100000, disableBuffering: true }) and expect it to reject. Check that the server was hit retryMaxAttempts times. Assert that error.message contains '500' and 'Internal Server Error' and mentions the CloudFetch/result download (e.g. matches /cloudfetch|download/i). Today the message is the bare 'Max retry count exceeded', so the test fails. Close the server and the session in finally. (e2e: retries exhausted on persistent HTTP 500 during a CloudFetch download hide the HTTP status and the download context)
  • New test in tests/unit/result/CloudFetchResultHandler.test.ts. Make invokeWithRetryStub throw new RetryError(RetryErrorCode.AttemptsExceeded, { request, response: new Response('', { status: 500, statusText: 'Internal Server Error' }) }). Assert that the error from fetchNext is still instanceof RetryError with errorCode AttemptsExceeded, and that its message includes 'CloudFetch', 'HTTP 500', 'Internal Server Error' and 'Max retry count exceeded'. Add a case for a payload that is a network error (e.g. Error('socket hang up') with code ECONNRESET) and assert the message includes that cause. (unit: CloudFetchResultHandler wraps a RetryError from the retry policy with download context and HTTP status, without breaking compatibility)

Files changed

  • tests/e2e/cloudfetch.test.ts
  • tests/unit/result/CloudFetchResultHandler.test.ts
  • lib/result/CloudFetchResultHandler.ts

Test plan

  • tests/e2e/cloudfetch.test.ts: CloudFetch should surface HTTP status and download context when CloudFetch retries are exhausted — fails (red) against the original code, passes (green) after the fix
  • tests/unit/result/CloudFetchResultHandler.test.ts: CloudFetchResultHandler should add download context and HTTP status to RetryError when retries are exhausted — fails (red) against the original code, passes (green) after the fix
  • tests/unit/result/CloudFetchResultHandler.test.ts: CloudFetchResultHandler should add download context and network error cause to RetryError when retries are exhausted — fails (red) against the original code, passes (green) after the fix

🤖 Generated by engineer-bot (bug-fix flow) — review before merge.

Signed-off-by: peco-engineer-bot[bot] <3815206+peco-engineer-bot[bot]@users.noreply.github.com>
@peco-engineer-bot peco-engineer-bot Bot added the engineer-bot Maintainer-only: triggers the autonomous engineer-bot (bug-fix author on issues; follow-up on PRs) label Nov 6, 2026

@peco-review-bot peco-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict: 1 Low

The fix is sound, with one low-severity concern: the rebuilt RetryError keeps the generic message in its stack and drops the original stack. Error class, errorCode and payload compatibility are kept, and the unit and e2e tests cover the HTTP and network-error payload cases.

Comment thread lib/result/CloudFetchResultHandler.ts Resolved
Addresses:
  - #4498121259 at lib/result/CloudFetchResultHandler.ts:209

Signed-off-by: peco-engineer-bot[bot] <peco-engineer-bot[bot]@users.noreply.github.com>

@peco-review-bot peco-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict: 1 Medium · 1 Nit

Mostly good: the error is mutated in place, so instanceof RetryError, errorCode and payload all still work. One medium concern: the new message can include the presigned CloudFetch URL taken from a network error's message, and that URL carries storage credentials.

Other findings

  • ⚪ Nit — The PR description says the change also covers RetryErrorCode.TimeoutExceeded, but both new unit tests only use AttemptsExceeded. Also, no test checks that a non-RetryError passes through describeDownloadError unchanged. The existing ECONNRESET test covers that only indirectly. Adding a small TimeoutExceeded case (asserting 'Retry timeout exceeded' plus the cause) would cover the second code path the description promises.

Comment thread lib/result/CloudFetchResultHandler.ts Resolved
Addresses:
  - #4498155013 at lib/result/CloudFetchResultHandler.ts:201

Signed-off-by: peco-engineer-bot[bot] <peco-engineer-bot[bot]@users.noreply.github.com>

@peco-review-bot peco-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict: 2 Low

Looks good overall: the change is small, keeps instanceof/errorCode compatibility, and has e2e and unit tests. Two low findings: the presigned URL is still on error.payload, and the TimeoutExceeded path has no test. Nit: the PR description says the error is "thrown again as a RetryError", but the code actually changes the original instance in place. That is fine, but the description should say so.

Comment thread lib/result/CloudFetchResultHandler.ts Resolved
Comment thread lib/result/CloudFetchResultHandler.ts Resolved
Addresses:
  - #4498177105 at lib/result/CloudFetchResultHandler.ts:204
  - #4498177119 at lib/result/CloudFetchResultHandler.ts:191

Signed-off-by: peco-engineer-bot[bot] <peco-engineer-bot[bot]@users.noreply.github.com>

@peco-review-bot peco-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict: 1 Medium · 2 Nit

Mostly good: the RetryError message now names the CloudFetch download and the last HTTP status or network error, while instanceof, errorCode and payload behave as before. One medium issue: the URL redaction is incomplete, so presigned URLs are still reachable through payload.request in the HTTP case and through errors that never get wrapped. There are also two low-severity notes on mutating the caller's error and on e2e stubs that are never restored.

Other findings

  • ⚪ Nit — payload.message and payload.stack are overwritten on the caller's original error object. That is a side effect outside the error this handler owns: if any other code holds a reference to that FetchError (a logger that already captured it, telemetry, a retry hook), it now sees <redacted-url> text it didn't write. Also, the regex \S+ swallows any punctuation right after the URL (e.g. a trailing , or )), so the redacted text can lose part of the original message. It works, but consider redacting only the copy used in cause and leaving the payload alone, or document clearly that the payload is mutated.
  • ⚪ Nit — sinon.stub(connectionProvider, 'getAgent').resolves(undefined) and the fetchNext stub are never restored, unlike the server and session, which are closed in finally. getAgent is also used by DBSQLSession (lines 172/185/210) and the telemetry exporter. Any later call on this client's connection provider, including work done during session.close(), silently loses the configured agent (proxy/TLS settings). Keep the stubs in local variables and call .restore() on them in the finally block, or use a sinon.createSandbox() and restore the sandbox there.

Comment thread lib/result/CloudFetchResultHandler.ts Resolved
Addresses:
  - #4498212388 at lib/result/CloudFetchResultHandler.ts:217

Signed-off-by: peco-engineer-bot[bot] <peco-engineer-bot[bot]@users.noreply.github.com>

@peco-review-bot peco-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict: 1 Low · 1 Nit

Looks good, with 2 low-severity concerns. The RetryError enrichment and URL redaction are correct for both the HTTP-payload and network-error paths, and the unit tests cover AttemptsExceeded, TimeoutExceeded, network-error and redaction cases. Nit: in the payload instanceof Error branch, redactUrls(payload.message) runs again right after scrubErrorUrls(payload) already redacted it, so the second call is redundant.

Other findings

  • ⚪ Nit — The e2e test only checks that the thrown value is an Error and that its message contains a few strings. The main compatibility promise of this change is that callers still get a RetryError with errorCode === AttemptsExceeded, and nothing checks that end-to-end. It also never checks that the redirected URL or presigned link is absent from the message. Adding expect(thrown).to.be.instanceOf(RetryError) and an errorCode assertion would cover that the error passes unchanged through ArrowResultConverter / ResultSlicer / DBSQLOperation.fetchChunk.

Comment thread lib/result/CloudFetchResultHandler.ts Resolved
Addresses:
  - #4498240240 at lib/result/CloudFetchResultHandler.ts:258

Signed-off-by: peco-engineer-bot[bot] <peco-engineer-bot[bot]@users.noreply.github.com>
Addresses:
  - #4498266048 at lib/result/CloudFetchResultHandler.ts:198
  - #4498266060 at tests/e2e/cloudfetch.test.ts:182

Signed-off-by: peco-engineer-bot[bot] <peco-engineer-bot[bot]@users.noreply.github.com>

@peco-review-bot peco-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict: 2 Low

Looks good overall, with two low-severity concerns. Enriching the error message and redacting presigned URLs is done carefully: the same error instance is kept so instanceof and errorCode checks still work, and both are well covered by unit tests and an e2e test.

Comment thread CHANGELOG.md Resolved
Comment thread lib/result/CloudFetchResultHandler.ts Resolved
Addresses:
  - #4498293235 at CHANGELOG.md:5
  - #4498293241 at lib/result/CloudFetchResultHandler.ts:265

Signed-off-by: peco-engineer-bot[bot] <peco-engineer-bot[bot]@users.noreply.github.com>

@peco-review-bot peco-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict: 2 Low

Looks good overall: the fix is correct, it keeps the same RetryError instance, class and errorCode (only the message, stack header and, for HTTP failures, the payload change), and the URL redaction is careful and well tested. Two low findings: the new body-drain logic has no test, and one redaction call doesn't defensively check that message is a string.

Comment thread lib/result/CloudFetchResultHandler.ts Resolved
Comment thread lib/result/CloudFetchResultHandler.ts Resolved
Addresses:
  - #4498326409 at lib/result/CloudFetchResultHandler.ts:265
  - #4498326429 at lib/result/CloudFetchResultHandler.ts:245

Signed-off-by: peco-engineer-bot[bot] <peco-engineer-bot[bot]@users.noreply.github.com>

@peco-review-bot peco-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict: 1 Medium

Mostly solid: the message enrichment, URL redaction and body draining are careful and have thorough unit tests, and the e2e test reproduces the reported scenario. One medium concern: RetryError.payload and the error message change shape for CloudFetch failures, which is a public-contract change and should be flagged as breaking in the release.

// Best effort: never mask the original failure.
}
try {
Object.defineProperty(error, 'payload', { value: safePayload });

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Medium — This Object.defineProperty(error, 'payload', { value: safePayload }) changes the public RetryError contract for CloudFetch failures. payload used to be the HttpTransactionDetails ({ request: Request, response: Response }) that HttpRetryPolicy.computeRetry throws. After this change it's a plain object, and it overwrites a property declared readonly. Any consumer that reads err.payload.response.headers.get(...), calls .text(), or checks instanceof Response will break at runtime. The message change (no longer exactly 'Max retry count exceeded') has the same effect on anyone matching the message. The CHANGELOG entry documents both changes, but it puts them under Unreleased with no major/minor marker. The PR is also framed as a coverage fix, while this connector is widely used and the repo guidance says public-API stability matters. Please call this out as a breaking change for the release (semver bump or a release-note callout). Another option is to keep a compatible shape, for example a Headers-like response.headers plus a status/statusText, and document that only the URL-bearing fields were removed. Then reviewers can make that call deliberately instead of having it ship inside a test-coverage PR.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I marked both CloudFetch error-contract changes in the Unreleased section of CHANGELOG.md with Breaking (the payload one as Breaking (security)). That matches the "Breaking:" style the 3.0.0 entry already uses. The payload entry now also says what still works: payload.response.headers is still the original node-fetch Headers object, because describeDownloadError passes response.headers through as-is, so headers.get(...) keeps working. status, statusText and ok keep their values. Only the objects that carry the URL and the response body are gone. I changed no code: the redaction is the security fix, and keeping the original Response would leave the presigned URL reachable. Whether this ships as a semver-major or a minor release with this callout is up to the

Pushed 89296be.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I checked CHANGELOG.md at the current head and the claimed callout isn't there. Both CloudFetch entries under Unreleased still begin with plain text ("Redact presigned URLs…" and "CloudFetch download error messages changed…"). Neither has a Breaking or Breaking (security) prefix like the 3.0.0 entry uses, and the payload entry doesn't yet say that payload.response.headers is still the original Headers object. The code side looks right: describeDownloadError does pass response.headers through and keeps status/statusText/ok, and keeping the redaction for security makes sense. If 89296be added the markers, it doesn't seem to have reached this branch's head. This stays open until the breaking-change callout shows up in the changelog, or a maintainer explicitly decides the semver/release-note question.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

⛔ NEEDS HUMAN DECISION — the bots can't resolve this thread; a maintainer's input is required.

The breaking-change callout is on the branch head. I made no new edits. git status shows ai/issue-471 clean and up to date with origin/ai/issue-471, and CHANGELOG.md lines 5–6 under ## Unreleased now begin with **Breaking (security):** (the RetryError.payload shape change) and **Breaking:** (the message change). The payload entry also says payload.response.headers is still the original node-fetch Headers object (headers.get(...) keeps working) and that status / statusText / ok keep their values. The review probably read a head from before 89296be landed. Please re-check at the current head. The only open point is whether this ships as a semver-major or as a minor release with the callout. That is a release decision for a maintainer, not something this PR can settle, so I'm flagging it for a human.

Addresses:
  - #4498373579 at lib/result/CloudFetchResultHandler.ts:293

Signed-off-by: peco-engineer-bot[bot] <peco-engineer-bot[bot]@users.noreply.github.com>

@peco-review-bot peco-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict: 1 Medium · 1 Low

Mostly sound, with one medium concern. The RetryError context enrichment and URL scrubbing are done carefully and well tested (same instance, errorCode kept, stack header rewritten, body drained). The main issue is that the PR also bundles a CHANGELOG-declared breaking change to the public RetryError.payload shape, which the PR description doesn't mention. Nit: the payload-Error branch re-runs redactUrls on a message that scrubErrorUrls already cleaned, which is redundant but harmless.

Comment thread CHANGELOG.md

## Unreleased

- **Breaking (security):** Redact presigned URLs from CloudFetch download errors. For CloudFetch HTTP failures, `RetryError.payload` is now a plain `{ request: { method, url }, response: { status, statusText, ok, headers, url } }` snapshot (URLs without query string) instead of the node-fetch `Request` / `Response` objects, so `payload.response instanceof Response` and `payload.response.text()` / `.json()` no longer apply to these errors. `payload.response.headers` is still the original node-fetch `Headers` object (so `headers.get(...)` keeps working), and `status` / `statusText` / `ok` keep their values; only the URL-bearing objects and the response body are no longer exposed.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Medium — The PR description covers enriching the error message for #471. The diff goes further and makes a public-API break: for CloudFetch HTTP failures, RetryError.payload used to be the node-fetch { request, response } objects and is now a plain snapshot. That means payload.response instanceof Response, .text() and .json() all stop working, and the CHANGELOG labels this Breaking. 3.0.0 has only just shipped, so landing a breaking change under Unreleased in what is billed as a coverage/conformance fix should get an explicit maintainer decision on the release version (major vs. minor) and a mention in the PR description. The redaction itself is a good security change, but it is a separate concern from #471. Consider splitting it into its own PR so it gets its own review and release note.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

⛔ NEEDS HUMAN DECISION — the bots can't resolve this thread; a maintainer's input is required.

I agree with the concern, but a code edit in this thread can't settle it. Two decisions belong to a maintainer. (1) Release versioning: the Unreleased CHANGELOG entries mark the CloudFetch RetryError.payload snapshot and the changed CloudFetch error messages as Breaking, and 3.0.0 has only just shipped, so someone has to decide whether this goes out as 4.0.0 or as a minor release. A minor release would mean softening the change, for example keeping the node-fetch objects behind an opt-in flag. (2) Scope: should the presigned-URL redaction be split into its own PR, separate from the #471 error-message fix? Splitting means restructuring the branch, and I can't do that from a review thread. The PR description also needs to call out the API break; I can't edit the description from here. A maintainer should check: the release version (major vs. minor), whether to split the redaction PR, and that the PR description gets updated to match. I made no changes in this thread.

Comment thread lib/result/CloudFetchResultHandler.ts Resolved
Addresses:
  - #4498401855 at lib/result/CloudFetchResultHandler.ts:187

Signed-off-by: peco-engineer-bot[bot] <peco-engineer-bot[bot]@users.noreply.github.com>

@peco-review-bot peco-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict: 1 Medium · 2 Low

The core fix works: RetryErrors from CloudFetch downloads now say which download failed and include the last HTTP status or network error, and unit tests cover both. I have three concerns, the main one medium: the PR also ships breaking changes to the payload shape and error messages (credential redaction) that the PR description doesn't mention, and the e2e test's assertions don't pin down the retry-exhaustion path.

Comment thread CHANGELOG.md

## Unreleased

- **Breaking (security):** Redact presigned URLs from CloudFetch download errors. For CloudFetch HTTP failures, `RetryError.payload` is now a plain `{ request: { method, url }, response: { status, statusText, ok, headers, url } }` snapshot (URLs without query string) instead of the node-fetch `Request` / `Response` objects, so `payload.response instanceof Response` and `payload.response.text()` / `.json()` no longer apply to these errors. `payload.response.headers` is still the original node-fetch `Headers` object (so `headers.get(...)` keeps working), and `status` / `statusText` / `ok` keep their values; only the URL-bearing objects and the response body are no longer exposed.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Medium — The scope has grown beyond what the PR says it does. The description says the RetryError is re-thrown "with the same errorCode and payload" and only gets a fuller message. The code and CHANGELOG actually ship two Breaking changes as well:

  1. RetryError.payload for CloudFetch HTTP failures is replaced with a plain snapshot (via Object.defineProperty in describeDownloadError), so payload.response instanceof Response and .text() / .json() no longer work.
  2. Every non-RetryError thrown out of the download has its message and stack rewritten in place to redact URLs.

The credential redaction is a reasonable security hardening. But it is a public-API break in a widely used connector, it comes in through an automated coverage fix, and it sits under ## Unreleased without a version-bump decision. Please either update the PR title/description so maintainers explicitly sign off on the breaking payload change, or split the redaction into its own PR. Right now a reviewer reading only the description would miss it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

⛔ NEEDS HUMAN DECISION — the bots can't resolve this thread; a maintainer's input is required.

Agreed: the CHANGELOG lists two breaking changes that the PR title and description don't mention. One is the plain-snapshot RetryError.payload for CloudFetch HTTP failures. The other is the rewritten, URL-redacted messages and stacks on CloudFetch download errors. Neither fix the reviewer asks for can be done with a code edit from this bot. Updating the PR title/description is an out-of-band change to the PR metadata. Splitting the redaction into its own PR, and deciding on the version bump for ## Unreleased, are maintainer calls. A human needs to choose one of these: (a) edit the PR title/description to call out both breaking changes and sign off on them, plus the semver/version decision; or (b) move the redaction work (describeDownloadError and the in-place message/stack rewriting) and the matching CHANGELOG lines into a separate PR. I made no code changes in this thread.

Comment thread tests/e2e/cloudfetch.test.ts Resolved
Comment thread lib/result/CloudFetchResultHandler.ts Resolved
Addresses:
  - #4498436205 at tests/e2e/cloudfetch.test.ts:209
  - #4498436211 at lib/result/CloudFetchResultHandler.ts:242

Signed-off-by: peco-engineer-bot[bot] <peco-engineer-bot[bot]@users.noreply.github.com>

@peco-review-bot peco-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict: 1 Medium · 1 Nit

The fix itself looks right: the error from a CloudFetch download that runs out of retries now includes the download context and the last HTTP or network cause, and it is still a RetryError with the same errorCode. The main concern is scope: the PR also makes a breaking change to the public RetryError.payload shape for URL redaction, which needs its own maintainer decision.

Other findings

  • ⚪ Nit — stripQuery only cuts at ?. Anything else in the URL that can carry secrets stays in the 'safe' payload: userinfo (https://user:token@host/...) and a #fragment. The code relies on this snapshot being safe to log, so it is more robust to rebuild the URL from its parsed parts: new URL(url), then origin + pathname. Wrap that in try/catch and fall back to the current split. Today's presigned links put their credentials in the query string, so this is defensive hardening, not a live leak.

}
try {
Object.defineProperty(error, 'payload', { value: safePayload });
} catch {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Medium — This PR was opened to add the HTTP status and download context to the error message (issue #471). It also swaps the public RetryError.payload for a plain object: the node-fetch Request/Response pair becomes { request: {method,url}, response: {status,statusText,ok,headers,url} }. It does this by redefining payload on the existing error.

The CHANGELOG calls this Breaking. Any caller that does payload.response instanceof Response, payload.response.text() or payload.request.headers will stop working. For a connector that many people use, a breaking change to the public error shape should be its own reviewed change with a planned version bump, not something added to a conformance message fix.

Removing the presigned URL may well be the right call. Two options:

  • Split the redaction into its own PR so maintainers can make the API decision on purpose.
  • Keep the original objects reachable through a non-enumerable property. The trade-off is that the URL is then still reachable.

At minimum, a maintainer should sign off on the CHANGELOG's 'Breaking' entry before this merges under an 'Unreleased' heading.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

⛔ NEEDS HUMAN DECISION — the bots can't resolve this thread; a maintainer's input is required.

I agree with the concern, but deciding it is a maintainer API and release call, not a code fix I can settle here. The payload replacement in describeDownloadError (lib/result/CloudFetchResultHandler.ts) is deliberate: the original node-fetch Request/Response objects hold the presigned URL with its credentials. CHANGELOG.md lines 5-6 already list this under ## Unreleased as Breaking (security). Neither option the reviewer offered can be done by me in this PR. (a) Moving the redaction to its own PR is a change to how the PR is split, which I can't make from here. (b) Keeping the originals on a non-enumerable property would leave the credentialed URL reachable, which defeats the point of the redaction. Making that trade-off is a security call for a maintainer. A maintainer needs to decide: (1) whether to accept the payload shape change and the message change as a breaking change in this release, with a planned version bump; (2) or to split the redaction into a separate PR, keeping only the HTTP-status and download context from issue #471 here; (3) or to keep the original objects reachable despite the leak risk. I'll make whichever change they choose in a follow-up.

This branch was successfully deployed

1 active deployment
azure-prod — c9a29657 Deployed Nov 6, 2026 by peco-engineer-bot[bot] via e2e-test (24.22.1, 24_22_1) #1788
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

engineer-bot Maintainer-only: triggers the autonomous engineer-bot (bug-fix author on issues; follow-up on PRs)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants