A tag with a falsy ID is never invalidated

Author: petr-kratochvilCreated Aug 28, 2026Updated Aug 28, 2026

Summary

When a tag has a falsy ID, it is treated as without ID on writing, but as with ID on read and cleanup.

Consequences:

  • The tag is never effectivelly invalidated by such ID
  • The stale query references accumulate (instead of being cleaned up after unsubscribe)

Minimal reproduction

Tested on: "@reduxjs/toolkit": "2.12.0". Codesandbox project: https://codesandbox.io/p/devbox/t5myq5 (please look at the reproduction.js source file)

Use case

My use case was using a numeric ID starting with zero, which is a falsy value. The same behavior happens when using an empty string as the tag ID.

Using 0 or "" (empty string) as a tag ID is a legal use based on the Typescript definition:

typescript
export type FullTagDescription<TagType> = {
  type: TagType
  id?: number | string
}

(packages/toolkit/src/query/endpointDefinitions.ts)

The cause

For a falsy tag ID, the following paths disagree:

  1. Write path: id || '__internal_without_id' - falsy ID is regarded as tag without ID (packages/toolkit/src/query/core/buildSlice.ts)
  2. Read path: (tag.id !== undefined ? ... : ...) - falsy ID (which is not undefined) is regarded as a valid ID (packages/toolkit/src/query/core/buildSelectors.ts)
  3. Cleanup path: tag.id ?? '__internal_without_id' - falsy ID (which is not null/undefined) is regarded as a valid ID, so the original __internal_without_id bucket does not get cleaned up. (packages/toolkit/src/query/core/buildSlice.ts)

Suggested fix

For consistency, the same policy should be used in all places regarding falsy values. A minimal fix is to use tag.id ?? '__internal_without_id' in the write path.