medusajs/medusa

[Bug]: Stale cache served for list responses with caching feature flag enabled

Open

#14903 opened on Mar 17, 2026

View on GitHub
 (3 comments) (1 reaction) (0 assignees)TypeScript (22,539 stars) (2,090 forks)batch import
good first issuetype: bugversion: 2.0

Description

Package.json file

"dependencies": {
    "@asteasolutions/zod-to-openapi": "^7.3.4",
    "@aws-sdk/client-s3": "^3.992.0",
    "@aws-sdk/client-sesv2": "^3.992.0",
    "@aws-sdk/credential-providers": "^3.992.0",
    "@grpc/grpc-js": "^1.14.0",
    "@medusajs/admin-sdk": "2.13.1",
    "@medusajs/cli": "2.13.1",
    "@medusajs/event-bus-redis": "^2.13.1",
    "@medusajs/framework": "2.13.1",
    "@medusajs/medusa": "2.13.1",
    "@medusajs/workflows-sdk": "^2.13.1",
    "@opensearch-project/opensearch": "^3.5.1",
    "@types/sharp": "^0.31.1",
    "axios": "^1.13.5",
    "date-fns": "^4.1.0",
    "dd-trace": "^5.81.0",
    "grpc-web": "^1.5.0",
    "jsonwebtoken": "9.0.3",
    "jwks-rsa": "^3.2.0",
    "kafkajs": "^2.2.4",
    "lodash.merge": "^4.6.2",
    "openai": "^6.16.0",
    "papaparse": "^5.5.3",
    "react-easy-crop": "^5.5.6",
    "sharp": "^0.34.5",
    "swagger-ui-express": "^5.0.1",
    "winston": "^3.18.3",
    "zod": "^3.25.76"
  },
  "devDependencies": {
    "@medusajs/test-utils": "2.13.1",
    "@swc/core": "^1.7.28",
    "@swc/jest": "^0.2.36",
    "@types/jest": "^29.5.13",
    "@types/kafkajs": "^1.8.2",
    "@types/lodash.merge": "^4.6.9",
    "@types/multer": "^2.0.0",
    "@types/node": "^20.0.0",
    "@types/react": "^18.3.2",
    "@types/react-dom": "^18.2.25",
    "@types/supertest": "^6.0.3",
    "@types/swagger-ui-express": "^4.1.8",
    "@types/winston": "^2.4.4",
    "cross-env": "^7.0.3",
    "husky": "^9.1.7",
    "jest": "^29.7.0",
    "prettier": "^3.5.3",
    "prop-types": "^15.8.1",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "supertest": "^7.1.1",
    "ts-node": "^10.9.2",
    "typescript": "^5.6.2",
    "vite": "^5.2.11",
    "yalc": "^1.0.0-pre.53"
  },

Node.js version

v22

Database and its version

irrelevant, but v18.3

Operating system name and version

Every

Browser name

No response

What happended?

Hello All, When enabling featureFlags: { caching: true }, list endpoints, i.e. /store/products return stale data after entity updates that affect filter-relevant fields.

Steps to reproduce:

Enable caching in medusa-config:

featureFlags: {
  caching: true,
}

Create a product in draft status Hit GET /store/products (response gets cached, draft product is excluded by status filter) Publish the product via the admin API Hit GET /store/products again Expected: The newly published product appears in the response.

Actual: The cached response from step 3 is returned. The published product is missing until the cache TTL expires.

Expected behavior

The list cache gets properly invalidated.

Actual behavior

Possible root cause:

buildAffectedCacheKeys in packages/modules/caching/src/utils/parser.ts only generates Entity:list:* invalidation tags for created and deleted operations:

if (entity.isInArray || ["created", "deleted"].includes(operation)) {
  keys.add(⁠ ${entity.type}:list:* ⁠)
}

When a product.updated event fires, the invalidation only produces Product:prod_xyz as a tag. Since the product wasn't part of any cached list response (it was filtered out while in draft), no cached entry has that tag associated with it. The list wildcard tag that would actually clear the stale response is never generated.

This isn't limited to products - it affects every entity type where cached list queries use filters. Any update that changes whether an entity matches a given filter (status changes, category reassignment, sales channel changes, etc.) will result in stale list responses.

Suggested fix:

Always include the list wildcard tag regardless of operation type:

keys.add(⁠ ${entity.type}:list:* ⁠) The cache layer has no way to know which fields are filter-relevant, so the safe default should probably be to invalidate list caches on any mutation. Individual entity caches (Product:prod_123) would still be preserved for updates that only affect a single entity lookup. I'm not familiar with Medusa source code though, so there might be better ways, I think.

Link to reproduction repo

can be reproduced in likely every single medusa instance with caching enabled

Contributor guide