Skip to content

fix(source-salesforce): let REST streams refresh their token and restart the query when the session is invalidated mid-pagination - #84302

Open
James Truty (jtruty) wants to merge 4 commits into
airbytehq:masterfrom
jtruty:jtruty/source-salesforce-rest-locator-session-restart
Open

fix(source-salesforce): let REST streams refresh their token and restart the query when the session is invalidated mid-pagination#84302
James Truty (jtruty) wants to merge 4 commits into
airbytehq:masterfrom
jtruty:jtruty/source-salesforce-rest-locator-session-restart

Conversation

@jtruty

@jtruty James Truty (jtruty) commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

What

A Salesforce query locator belongs to the session that created it. When that session is invalidated while a REST stream is paginating, SalesforceErrorHandler returns RETRY, so the CDK retries the same nextRecordsUrl (and on the REST path it could not even refresh the token first, see below). A new session cannot resume another session's locator, so every retry fails with INVALID_SESSION_ID, the stream exhausts its attempts, and the sync fails.

This is routine with Refresh Token Rotation enabled, where each token exchange ends the previous session. Since 2.7.20 the connector re-logs in roughly every 30 minutes during a sync, so any stream on the REST path whose read outlives that interval will have a locator open when a refresh lands. Streams using the Bulk API are unaffected, so this shows up on objects that cannot use Bulk, for example anything containing a compound field such as an address.

Diagnosed on a production connection where every attempt died the same way: the read progresses normally, then Refreshing Salesforce OAuth token (N s since last login) is followed 2-5 seconds later by repeated Salesforce session expired or invalid. Token has been refreshed. until the stream gives up. 13 of 13 attempts on one job, always the same stream, while every Bulk-path stream in the same sync completed. Another connection with a larger and longer all-Bulk sync re-logged in twice mid-run in the same window with zero session errors, which isolates it to the REST pagination path rather than to size or duration.

Pre-RTR the same re-login was harmless: two earlier jobs on the same connection re-logged in mid-sync with no rotation persisted, no session errors, and both succeeded. So the trigger is the rotation ending the session, not the refresh itself.

How

Two defects, fixed together because neither is safe or sufficient alone (see the ordering note below).

REST streams could not refresh. They authenticated with TokenAuthenticator(sf_object.access_token) (source.py), a token captured when the stream was built, and their HttpClient error handler was built without a token_provider (streams.py). So they never triggered refresh_access_token_if_stale(), never observed a refresh, and INVALID_SESSION_ID could not force one. Only the Bulk path had a provider, wired through BearerAuthenticator. They now use the CDK's BearerAuthenticator with the same SalesforceTokenProvider the Bulk path uses, and the error handler is given that provider so the existing force-refresh and fail-fast paths apply.

An orphaned locator was retried rather than restarted.

  • SalesforceErrorHandler.interpret_response raises QueryLocatorExpiredException when the failing request is a locator request, after the existing token refresh. An ordinary expired session is still retried exactly as before. Detection matches on the URL path only so a SOQL query string cannot produce a false positive, following the existing _is_bulk_job_status_check pattern.
  • _read_pages catches it and restarts the query after the last primary key read for that property chunk, tracked per chunk so chunks resume independently.
  • A restart that reads nothing before its locator expires again fails the attempt rather than restarting from the same position forever.
  • Paths that cannot resume (no string primary key, UNSUPPORTED_FILTERING_STREAMS, parent objects, or nothing read yet) raise a transient AirbyteTracedException instead of a bare error, preserving the readable failure the previous RETRY resolution produced.
  • IncrementalRestSalesforceStream.request_params now emits ORDER BY <pk> ASC, which is what makes the resume position sound.

Review guide

  1. source_salesforce/source.py — the provider-backed BearerAuthenticator in generate_streams.
  2. source_salesforce/streams.py — the token_provider now passed to the REST error handler, plus _read_pages restart and guard, supports_query_restart, and the ORDER BY in IncrementalRestSalesforceStream.request_params.
  3. source_salesforce/rate_limiting.py — the new exception and the locator detection.
  4. unit_tests/api_test.py — five pagination tests plus two for the authenticator and error-handler wiring.

Two things worth a second opinion:

The ORDER BY is a new clause for most REST streams, not an alignment with existing behaviour. Streams without a replication key already ordered by primary key via RestSalesforceStream.request_params. Streams with one did not, in either the sliced branch or the no-cursor branch used when they are read in full refresh mode. On a very large object this could lead the query optimizer to favour the Id index over the cursor index and surface as QUERY_TIMEOUT. GET /services/data/vXX.X/query/?explain=<SOQL> reports leadingOperationType and relativeCost per candidate plan and would settle it in two calls, but that needs a large org I do not have access to. It cannot be added lazily either: a resume position requires deterministic order from the query's first page.

Alternative considered and rejected: re-read the current slice from its start and let primary-key dedup absorb the overlap. That needs no ORDER BY and carries no plan risk, but it livelocks on exactly the case this fixes, since any slice whose read exceeds the refresh interval can never complete, and for a full refresh stream the slice is the whole stream. It is also unsafe here: the multi-chunk stitcher counts parts per primary key, so re-reading an already-counted chunk can emit records missing other chunks' fields. Happy to switch approach if you would rather not change query shape on this connector.

Also noted while working here, left alone as out of scope: when a parent-object WHERE clause is present in RestSalesforceStream.request_params, ORDER BY is concatenated without a separating space. It looks unreachable today because those streams are in UNSUPPORTED_FILTERING_STREAMS.

What made this reachable, and why it is rare

The trigger is narrower than "a long REST read":

  • Before this PR, refresh_access_token_if_stale() was only reachable from the Bulk path. SalesforceTokenProvider is wired into BulkSalesforceStream._instantiate_declarative_stream via BearerAuthenticator (streams.py:647), and SalesforceTokenProvider.get_token() is its only caller.
  • REST streams authenticated with TokenAuthenticator(sf_object.access_token), a token captured once at construction, so they neither triggered a proactive refresh nor observed one.
  • Their HttpClient error handler had no token_provider, so on INVALID_SESSION_ID the force_refresh() call was skipped — the Token has been refreshed. in that message was inaccurate on this path.

So the failure needs a Bulk stream requesting a token past the refresh interval while a REST stream is mid-pagination. That matches what we see in production: a connection with three heavy concurrent Bulk streams failed on every attempt, while another making 300+ locator calls across a 90-minute sync, with little late Bulk activity, has never hit it in 30 days.

Why these two are one PR and not two

I originally split them and then merged them back, because the split creates a state that must never exist: REST streams that refresh but cannot restart.

  • Authenticator without restart is harmful. Giving REST streams the provider makes them call refresh_access_token_if_stale(), so a long REST read that today never refreshes would start rotating at the 30 minute mark and invalidate its own open locator. Syncs that are healthy today would begin failing.
  • Restart without authenticator is incomplete. A REST read that outlives the org session timeout (2 hours by default) gets INVALID_SESSION_ID with no Bulk stream involved; the restarted query presents the same dead token, makes no progress, and the guard fails the attempt.

Splitting would let that bad state arise from a wrong merge order or from reverting only the restart half later. Together they are order-independent and revert as a unit. If you would still rather review them separately, say so and I will split with the ordering constraint made explicit.

User Impact

REST-path streams whose reads outlive the token refresh interval can complete instead of failing. Before this change, with RTR enabled such a stream could not finish at all, and the job burned its full retry budget re-reading the same data.

Behaviour changes for users:

  • Incremental REST queries, and full refresh reads of objects with a replication key, now carry ORDER BY <pk> ASC. Emitted record order changes accordingly; state logic is order-insensitive, so there is no correctness impact.
  • A restart re-queries from the last primary key, so a small number of duplicate records is possible if the underlying data shifts, consistent with how the connector already tolerates overlap from lookback_window.
  • Failures that cannot be resumed now surface as a transient error naming the stream, rather than as an exhausted-retries error.

Can this PR be safely reverted and rolled back?

  • YES 💚

…dated mid-pagination

A Salesforce query locator belongs to the session that created it. When that
session is invalidated while a REST stream is paginating, the error handler
refreshes the token and retries the same `nextRecordsUrl`, which can never
succeed: the stream exhausts its retries and fails the sync.

This is routine with Refresh Token Rotation, where every token exchange ends
the previous session. The connector refreshes proactively every 30 minutes, so
any REST stream whose read takes longer than that is affected. Streams on the
Bulk API path are not.

The error handler now raises for a locator request so the caller can tell the
difference between an ordinary expired session and a dead locator, and
`_read_pages` restarts the query after the last primary key it read for that
property chunk. A restart that reads nothing before its locator expires again
fails the attempt rather than restarting from the same position forever, and
the paths that cannot resume raise a transient `AirbyteTracedException` instead
of a bare error.

The resume position is only sound for a query ordered by primary key, so
`IncrementalRestSalesforceStream` now emits `ORDER BY <pk> ASC`. Note this is a
new clause for those streams, including when they are read in full refresh mode
via the no-cursor branch; only streams without a replication key, which use
`RestSalesforceStream.request_params`, already ordered this way.
@github-actions

Copy link
Copy Markdown
Contributor

👋 Welcome to Airbyte!

Thank you for your contribution from jtruty/airbyte! We're excited to have you in the Airbyte community.

If you have any questions, feel free to ask in the PR comments or join our Slack community.

💡 Show Tips and Tricks

PR Slash Commands

As needed or by request, Airbyte Maintainers can execute the following slash commands on your PR:

  • /format-fix - Fixes most formatting issues.
  • /bump-version - Bumps connector versions.
  • /run-connector-tests - Runs connector tests.
  • /run-cat-tests - Runs CAT tests.
  • /run-regression-tests - Runs regression tests for the modified connector(s).
  • /build-connector-images - Builds and publishes a pre-release docker image for the modified connector(s).
  • /publish-connectors-prerelease - Publishes pre-release connector builds (tagged as {version}-preview.{git-sha}) for all modified connectors in the PR.
  • /ai-review - AI-powered PR review for connector safety and quality gates.
  • /ai-docs-review - AI-powered documentation review for PRs with connector changes.
  • /ai-create-docs-pr - Creates a documentation PR for connector changes.
  • /force-merge reason="<A_GOOD_REASON>" - Force merges the PR using admin privileges, bypassing CI checks. Requires a reason.

Tips for Working with CI

  1. Pre-Release Checks. Please pay attention to these, as they contain standard checks on the metadata.yaml file, docs requirements, etc. If you need help resolving a pre-release check, please ask a maintainer.
    • Note: If you are creating a new connector, please be sure to replace the default logo.svg file with a suitable icon.
  2. Connector CI Tests. Some failures here may be expected if your tests require credentials. Please review these results to ensure (1) unit tests are passing, if applicable, and (2) integration tests pass to the degree possible and expected.
  3. (Optional.) BYO Connector Credentials for tests in your fork. You can optionally set up your fork with BYO credentials for your connector. This can significantly speed up your review, ensuring your changes are fully tested before the maintainers begin their review.
📚 Show Repo Guidance

Helpful Resources

📝 Edit this welcome message.

REST streams authenticated with `TokenAuthenticator(sf_object.access_token)`,
a token string captured when the stream was built, and their error handler was
built without a token provider. They could therefore neither trigger a
proactive refresh nor observe one, and `INVALID_SESSION_ID` could not force a
refresh, so a REST read that outlived the org session timeout had no way to
recover.

They now read from the same `SalesforceTokenProvider` the Bulk path uses, and
the error handler is given that provider so the existing force-refresh and
fail-fast paths apply.

This must land together with the query restart in the same PR. On its own it
makes REST streams start refreshing at the 30 minute mark, which rotates the
token and invalidates any locator they hold, so long REST reads that survive
today would begin failing without the restart to recover them.
@jtruty James Truty (jtruty) changed the title fix(source-salesforce): restart REST query when the session is invalidated mid-pagination fix(source-salesforce): let REST streams refresh their token and restart the query when the session is invalidated mid-pagination Aug 12, 2026
… streams

The custom authenticator added in the previous commit duplicated what
`BearerAuthenticator` already does: it is an `AuthBase` and reads the token
provider on every call, which is how the Bulk path is already wired.
@devin-ai-integration

Copy link
Copy Markdown
Contributor

↪️ Triggering /ai-prove-fix per Hands-Free AI Triage Project triage next step.

Reason: Draft community fix; the only red checks are the no-credentials connector test on a fork PR and the progressive rollout gate, neither of which this diff affects. Prove-fix validation is the next pipeline step for:
https://github.com/airbytehq/oncall/issues/13237

Devin session

@octavia-bot

octavia-bot Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

🔍 AI Prove Fix session starting... Running readiness checks and testing against customer connections. View playbook

Devin AI session created successfully!

@airbyte-support-bot

Airbyte Support Bot (airbyte-support-bot) commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

🧪 Fix Validation (/ai-prove-fix) — ⚠️ No Regression Detected, Fix Not Exercised

The change is safe as far as this can tell, but nothing here proves it fixes the reported failure: the code path it changes was never entered by any test that could be run.

Pre-release: airbyte/source-salesforce:2.8.2-preview.b2891dc

Regression test — no regression

Comparison run, target built from b2891dc vs control 2.8.1, integration-test credentials, all streams:

  • SPEC, CHECK, DISCOVER: pass.
  • READ: both versions exit cleanly with 14,311 records each, and per-stream counts are identical across all 11 streams. No stream lower on the target, none empty on both.
  • The run's overall red is not a regression from this PR: the comparison validator rejects duplicate primary keys that appear identically in both versions, on AppDefinition and FormulaFunctionAllowedType (one each). Pre-existing and unrelated to this change.

Notably, the new ORDER BY <pk> ASC on incremental REST streams produced no record-count or content difference here. That is reassuring but says nothing about query plans on a large org — see below.

Why the fix itself is unproven

The target's logs contain no INVALID_SESSION_ID, no nextRecordsUrl, and none of the new restart logging, so the REST locator path was never reached. That is expected rather than surprising: per this PR's own analysis, reproducing it needs a Refresh Token Rotation-enabled org and a Bulk stream requesting a token past the 30 minute refresh interval while a REST stream is mid-pagination. Integration-test credentials do not provide that.

Escalation to a production connection was attempted and came up empty: across 14 days of failed source-salesforce sync attempts (all tiers, 46 distinct connections), no failure summary contains INVALID_SESSION_ID. The two candidates with repeated source-side failures were checked — one fails on an unrelated Bulk API filtering restriction, and the other could not be evaluated because Cloud log retrieval kept failing. Neither reproduces the signature. Nothing was pinned and no connection was touched.

James Truty (@jtruty) — you diagnosed this on a production connection where every attempt died the same way. That connection is the only known reproduction and it is not identified here. If you can point me at it, canarying 2.8.2-preview.b2891dc there (with the usual approval before any pin) would give real proving evidence; otherwise this merges on unit tests plus the no-regression result above.

Pre-flight and review notes

Pre-flight: PASSED
  • Viability — the diff matches the diagnosis: provider-backed BearerAuthenticator for the REST path, token_provider passed to the REST error handler, QueryLocatorExpiredException raised only for GET requests whose path matches a locator URL, and a per-property-chunk restart in _read_pages guarded against progress-free restart loops.
  • Safety — no suspicious code, no new external endpoints, no credential handling changes beyond reusing the existing shared token provider.
  • Breaking changenot breaking. No schema, spec, primary key, cursor, state format, or stream inventory changes, and no data-scope reduction. Patch bump 2.8.12.8.2 is correct, changelog entry present, progressive rollout remains enabled.
  • Reversibility — reversible. State written by the target is readable by the control, so a rollback needs no migration. The two halves of this fix also revert as a unit, which matches the reasoning in the PR description for keeping them together.
  • CI — every red check on this PR is a fork credential limitation, not a code failure: the two Test source-salesforce Connector [No Creds] failures are FileNotFoundError on secrets/config.json and secrets/config_sandbox.json, and the Progressive Rollout Gate fails on GCP_PROD_DB_ACCESS_CREDENTIALS environment variable is not set. Unit tests, lint, format, changelog and docs checks pass.

⚠️ Carry the author's ORDER BY concern into rollout. Incremental REST streams now carry ORDER BY <pk> ASC where they previously did not, including when read in full refresh mode. On a very large object this can lead the optimizer to favour the Id index over the cursor index and surface as QUERY_TIMEOUT. Test-sized data cannot rule that out, so progressive rollout is where it would show up — start slow and watch for QUERY_TIMEOUT on high-volume REST streams. Daily hands-free triage will monitor rollout health after release.

Once merged, /ai-canary-prerelease is the natural next step, ideally against a connection that actually exercises RTR.

Detailed evidence, including the connection identifiers examined, is recorded privately in airbytehq/oncall issue number 13300.


Devin session

@airbyte-support-bot Airbyte Support Bot (airbyte-support-bot) added the hyd-prove Hydra: ai-prove-fix stage has run label Aug 13, 2026
@github-actions

github-actions Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Pre-release Connector Publish Started

Publishing pre-release build for connector source-salesforce.
PR: #84302

Pre-release versions will be tagged as {version}-preview.b2891dc
and are available for version pinning via the scoped_configuration API.

View workflow run
Pre-release Publish: SUCCESS

Docker image (pre-release):
airbyte/source-salesforce:2.8.2-preview.b2891dc

Docker Hub: https://hub.docker.com/layers/airbyte/source-salesforce/2.8.2-preview.b2891dc

Registry JSON:

@devin-ai-integration

Copy link
Copy Markdown
Contributor

↪️ Triggering /ai-review per Hands-Free AI Triage Project triage next step.

Reason: /ai-prove-fix found no regression on current HEAD b2891dc9 (the changed path could not be exercised by any runnable test), so AI review is the next pipeline step. Left as a draft because this is an external contributor's fork.
https://github.com/airbytehq/oncall/issues/13237

Devin session

@octavia-bot
octavia-bot Bot marked this pull request as ready for review August 13, 2026 12:24
@octavia-bot

octavia-bot Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

AI PR Review starting...

Reviewing PR for connector safety and quality.
View playbook

Devin AI session created successfully!

@airbyte-support-bot

Airbyte Support Bot (airbyte-support-bot) commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

🛡️ AI PR Review Report

🟡 Review Action: No Automated Review Submitted (UNKNOWN)

Two enforced gates could not be resolved from the available evidence, so no automated approval or change request is submitted. A maintainer decision is needed.

🟠 Risk Level: 4 / 5

Functional runtime change to REST pagination and auth: streams.py adds per-chunk query restart state and an unconditional ORDER BY <primary_key> ASC to REST SOQL queries, rate_limiting.py raises a new QueryLocatorExpiredException on INVALID_SESSION_ID for nextRecordsUrl requests, and source.py swaps the captured TokenAuthenticator for a provider-backed BearerAuthenticator. Unit coverage is good but the fixed path was never exercised against a live org and the query-plan impact of the new ORDER BY on large orgs is unmeasured.

Gate Summary

8 of 12 gates passed. Non-passing gates:

Gate Status Enforcement Summary
Per-Record Performance ⚠️ WARNING Warning-only The record loop in streams.py now evaluates the supports_query_restart property and tracks last_primary_key per record.
Forwards Compatibility ⚠️ WARNING Warning-only Pagination control flow changed, but the restart state (last_primary_key, restart_after_primary_key, last_restart_primary_key) lives only in memory on PropertyChunk, so persisted state stays compatible and a rollback to 2.8.1 is safe.
Behavioral Changes ⚠️ WARNING Warning-only Three user-visible behavior changes: (1) REST SOQL queries now always append ORDER BY <primary_key> ASC for restart-eligible streams, whose query-plan impact on large orgs is untested (acknowledged in the PR body); (2) INVALID_SESSION_ID on a nextRecordsUrl now restarts the query, or raises a transient AirbyteTracedException when it cannot restart, instead of retrying a URL that can only fail; (3) REST streams authenticate through SalesforceTokenProvider, so a mid-sync refresh (including refresh token rotation) is now picked up..
CI Checks ❓ UNKNOWN Enforced Lint, Format Check, Build and Verify Artifacts, Pre-Release Checks, Check Changelog Updated and the 145 unit tests all pass.
Live / E2E Tests ❓ UNKNOWN Enforced /ai-prove-fix concluded ⚠️ No Regression Detected, Fix Not Exercised: the regression comparison surfaced no INVALID_SESSION_ID, nextRecordsUrl or restart logging, so the changed code path was never entered and the fix is unproven against a live org..

No gate failed outright, so there is nothing to remediate before merge from this review's perspective. To resolve the two UNKNOWN gates:

  • Re-run the connector test suite with credentials (maintainer-triggered) so Test source-salesforce Connector [No Creds] reports on more than the unit tests.
  • Exercise the session-invalidation path against a live org (a long-running REST sync on an org with refresh token rotation enabled, or a forced session invalidation mid-pagination) so the fix is actually observed working, and measure the added ORDER BY on a large org.
📋 PR Details
  • PR: fix(source-salesforce): let REST streams refresh their token and restart the query when the session is invalidated mid-pagination #84302
  • Author: jtruty (community fork jtruty/airbyte)
  • Head SHA: b2891dc95ba0ee4a9bd20ac5537d75589d975bf2
  • Base: master
  • Labels: community, connectors/source/salesforce, hyd-prove, hyd-review
  • Connector: source-salesforce 2.8.1 → 2.8.2 (patch)
  • Changed files: 7 (+275 / -17)
    • source_salesforce/rate_limiting.pyQueryLocatorExpiredException, locator-path detection, token refresh on INVALID_SESSION_ID
    • source_salesforce/source.pyTokenAuthenticatorBearerAuthenticator(SalesforceTokenProvider(...))
    • source_salesforce/streams.py — per-chunk restart state, restart WHERE/ORDER BY clauses, no-progress guard, transient errors
    • unit_tests/api_test.py — 7 new tests
    • metadata.yaml, pyproject.toml, docs/integrations/sources/salesforce.md
  • Human reviews: none. Existing comments are all from bots.
🔍 Gate Evaluation Details

PR Hygiene — ✅ PASS (enforced)

Semantic title, detailed body describing the failure and fix, version bumped in metadata.yaml and pyproject.toml (2.8.1 -> 2.8.2), docs changelog row added for 2.8.2, no unresolved human review comments (all existing comments are bots).

Code Hygiene — ✅ PASS (warning-only)

Connector code and unit tests changed together; new exception, restart-state fields and guards carry explanatory docstrings/comments; no debug leftovers, dead code or TODOs in the diff.

Test Coverage — ✅ PASS (enforced)

unit_tests/api_test.py adds 7 targeted tests: authenticator reads the current token, error handler can force a refresh, restart on locator/session expiry, restart stitched across property chunks, repeated restarts while making progress, transient error when restart is impossible, and give-up when a restart makes no progress. 145 unit tests pass in CI.

Code Security — ✅ PASS (enforced)

No secrets logged or added; access tokens are read through SalesforceTokenProvider instead of being captured once; no new network hosts and allowedHosts is unchanged. Note: the restart filter interpolates the last primary key into SOQL (WHERE Id > '<value>'), values that originate from Salesforce record ids, consistent with existing query construction in this connector.

Per-Record Performance⚠️ WARNING (warning-only)

The record loop in streams.py now evaluates the supports_query_restart property and tracks last_primary_key per record. Overhead is small but the property (isinstance check plus two set lookups) could be hoisted out of the loop.

Breaking Dependencies — ✅ PASS (warning-only)

No dependency or CDK version changes; pyproject.toml only bumps the connector version.

Backwards Compatibility — ✅ PASS (enforced)

No spec, schema, primary key, cursor field or state format change; no stream removed or renamed. Patch bump 2.8.2 is appropriate and no releases.breakingChanges entry or migration guide is required.

Forwards Compatibility⚠️ WARNING (warning-only)

Pagination control flow changed, but the restart state (last_primary_key, restart_after_primary_key, last_restart_primary_key) lives only in memory on PropertyChunk, so persisted state stays compatible and a rollback to 2.8.1 is safe. Restarting mid-slice can re-emit records already emitted in that slice, so dedup relies on primary-key dedup at the destination.

Behavioral Changes⚠️ WARNING (warning-only)

Three user-visible behavior changes: (1) REST SOQL queries now always append ORDER BY <primary_key> ASC for restart-eligible streams, whose query-plan impact on large orgs is untested (acknowledged in the PR body); (2) INVALID_SESSION_ID on a nextRecordsUrl now restarts the query, or raises a transient AirbyteTracedException when it cannot restart, instead of retrying a URL that can only fail; (3) REST streams authenticate through SalesforceTokenProvider, so a mid-sync refresh (including refresh token rotation) is now picked up.

Out-of-Scope Changes — ✅ PASS (warning-only)

All changes are confined to airbyte-integrations/connectors/source-salesforce/** and docs/integrations/sources/salesforce.md.

CI Checks — ❓ UNKNOWN (enforced)

Lint, Format Check, Build and Verify Artifacts, Pre-Release Checks, Check Changelog Updated and the 145 unit tests all pass. Test source-salesforce Connector [No Creds] fails only on the credential-dependent standard tests (FileNotFoundError: secrets/config.json and secrets/config_sandbox.json) which a fork PR cannot run, so the core test signal is incomplete and a maintainer re-run with credentials is needed. Progressive rollout gate/summary failures are outside this gate.

Live / E2E Tests — ❓ UNKNOWN (enforced)

/ai-prove-fix concluded ⚠️ No Regression Detected, Fix Not Exercised: the regression comparison surfaced no INVALID_SESSION_ID, nextRecordsUrl or restart logging, so the changed code path was never entered and the fix is unproven against a live org.

📚 Evidence Consulted
  • Full diff of b2891dc against master, read file by file.
  • CI snapshot for the head commit: 26 passed, 3 failed, 0 pending, 13 skipped.
  • Logs of Test source-salesforce Connector [No Creds] (job 94446352480): 145 passed unit tests, then 2 failed, 2 passed, 3 skipped standard tests, both failures FileNotFoundError on missing secrets/*.json.
  • /ai-prove-fix conclusion comment on this PR: ⚠️ No Regression Detected, Fix Not Exercised, based on pre-release airbyte/source-salesforce:2.8.2-preview.b2891dc and regression run https://github.com/airbytehq/airbyte-ops-mcp/actions/runs/31696643199.
  • PR review threads and issue comments via the GitHub API (no human feedback present).
  • Airbyte connector versioning and breaking-change guidance for the 2.8.1 → 2.8.2 bump.
❓ How to Respond
  • The two UNKNOWN gates are evidence gaps, not defects found in the code. Nothing in the diff was flagged as a failure.
  • A maintainer with credentials should re-run the connector tests, and ideally validate the session-invalidation path against a live org, before merging.
  • The two warning gates (Per-Record Performance, Behavioral Changes) and Forwards Compatibility do not block merge, but the new unconditional ORDER BY <primary_key> ASC on REST queries is worth a deliberate maintainer decision for large orgs.
  • Push a new commit to re-trigger this review against a new head SHA.

Automated review by Devin. Issue: #84302

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

Projects

Development

Successfully merging this pull request may close these issues.

3 participants