Skip to content

fix: don't swallow createAsyncThunk aborts that happen before pending#5314

Open
chatman-media wants to merge 1 commit into
reduxjs:masterfrom
chatman-media:fix/abort-before-pending-not-swallowed
Open

fix: don't swallow createAsyncThunk aborts that happen before pending#5314
chatman-media wants to merge 1 commit into
reduxjs:masterfrom
chatman-media:fix/abort-before-pending-not-swallowed

Conversation

@chatman-media

Copy link
Copy Markdown

Summary

If a thunk's AbortController is aborted before the pending action is dispatched, the abort is silently swallowed and the error is mislabeled as a ConditionError.

This happens in two cases:

  1. An already-aborted external signal is passed to the thunk action creator.
  2. abort() is called during an async condition (before it resolves).

In both cases the thunk currently throws:

{ name: 'ConditionError', message: 'Aborted due to condition callback returning false.' }

Because the error is named ConditionError, the resulting rejected action has meta.condition === true, so it is skipped by default (unless dispatchConditionRejection: true is set). The net effect:

  • No action is dispatched at all — the abort is silently swallowed.
  • The error is reported as a condition rejection, even though no condition callback ever returned false.

This is inconsistent with aborting the same signal a moment later, once the thunk is already running. That path correctly produces a rejected action with an AbortError and meta.aborted === true, and matches the documented “Canceling While Running” behavior:

After a thunk has been cancelled this way, it will dispatch (and return) a "thunkName/rejected" action with an AbortError on the error property.

Reproduction

const thunk = createAsyncThunk('test', async () => 42, {
  condition: () => true, // any options object triggers the skip
})

const signal = AbortSignal.abort()
const dispatch = vi.fn()
const result = await thunk(undefined, { signal })(dispatch, () => ({}), undefined)

// Before this PR:
//   dispatch was never called (abort swallowed)
//   result.error.name === 'ConditionError'
//   result.meta.condition === true

The fix

The condition check in createAsyncThunk previously collapsed two distinct cases into one:

if (conditionResult === false || abortController.signal.aborted) {
  throw { name: 'ConditionError', message: 'Aborted due to condition callback returning false.' }
}

It now distinguishes them:

  • condition returning falseConditionError (unchanged — still skipped by default).
  • Controller aborted → AbortError carrying the real abort reason, so the rejected action is dispatched and the error is labeled correctly, consistent with aborting after pending.

Tests

  • Added a regression test asserting an already-aborted external signal (with options present) dispatches a rejected action with an AbortError instead of being swallowed. This test fails on master (expected 'ConditionError' to be 'AbortError') and passes with the fix.
  • Updated two existing tests that previously asserted the swallowed/mislabeled behavior:
    • aborting during an async condition… (was async condition with AbortController signal first) now asserts a rejected action with an AbortError.
    • handles already aborted external signal now asserts 'External signal was aborted' (matching the sibling accepts external signal test) instead of the ConditionError message.

Full packages/toolkit test suite passes (60 files, 1055 tests).

Related to #3826 (the broader family of swallowed errors from condition/forceRefetch); this addresses the abort facet specifically. The genuine condition === false skip behavior is unchanged.

When a thunk's `AbortController` was aborted before the `pending` action was
dispatched - either because an already-aborted external `signal` was passed,
or because `abort()` was called while an async `condition` was still pending -
the resulting error was reported as a `ConditionError` with the message
"Aborted due to condition callback returning false."

Because `meta.condition` was then `true`, the `rejected` action was skipped by
default (unless `dispatchConditionRejection` was set), so the abort was
silently swallowed and the error was mislabeled even though no `condition`
callback ever returned `false`.

This was inconsistent with aborting the same signal moments later (once the
thunk is running), which correctly produces a `rejected` action with an
`AbortError`, and it contradicted the documented "Canceling While Running"
behavior.

The condition-check now only throws a `ConditionError` when `condition`
actually returns `false`. If the controller was aborted, it instead throws an
`AbortError` carrying the real abort reason, so the abort is reported and
dispatched consistently regardless of timing.
@codesandbox

codesandbox Bot commented Jun 13, 2026

Copy link
Copy Markdown

Review or Edit in CodeSandbox

Open the branch in Web EditorVS CodeInsiders

Open Preview

@netlify

netlify Bot commented Jun 13, 2026

Copy link
Copy Markdown

Deploy Preview for redux-starter-kit-docs ready!

Name Link
🔨 Latest commit 42e83ce
🔍 Latest deploy log https://app.netlify.com/projects/redux-starter-kit-docs/deploys/6a2d8f2f40021c0009cc6223
😎 Deploy Preview https://deploy-preview-5314--redux-starter-kit-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@codesandbox-ci

Copy link
Copy Markdown

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

Latest deployment of this branch, based on commit 42e83ce:

Sandbox Source
@examples-query-react/basic Configuration
@examples-query-react/advanced Configuration
@examples-action-listener/counter Configuration
rtk-esm-cra Configuration

@aryaemami59 aryaemami59 changed the title fix: don't swallow createAsyncThunk aborts that happen before pending fix: don't swallow createAsyncThunk aborts that happen before pending Jun 13, 2026
@aryaemami59 aryaemami59 added the RTK-Query Issues related to Redux-Toolkit-Query label Jun 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

RTK-Query Issues related to Redux-Toolkit-Query

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants