Skip to content

[Bug]: Native variant deletion leaves custom linked records active despite deleteCascade #16732

Description

@brunoocal

Package.json file

Backend package generated with create-medusa-app@2.20.1 --version 2.20.1. All Medusa packages below are unmodified npm releases. The root workspace configuration and pnpm lockfile are included in the reproduction repository.

{
  "name": "@dtc/backend",
  "version": "0.0.1",
  "description": "A starter for Medusa projects.",
  "author": "Medusa (https://medusajs.com)",
  "license": "MIT",
  "keywords": [
    "sqlite",
    "postgres",
    "typescript",
    "ecommerce",
    "headless",
    "medusa"
  ],
  "scripts": {
    "build": "medusa build",
    "start": "medusa start",
    "dev": "medusa develop",
    "lint": "medusa lint",
    "test:integration:http": "jest --runInBand"
  },
  "dependencies": {
    "@medusajs/admin-sdk": "2.20.1",
    "@medusajs/admin-shared": "2.20.1",
    "@medusajs/caching": "2.20.1",
    "@medusajs/cli": "2.20.1",
    "@medusajs/dashboard": "2.20.1",
    "@medusajs/draft-order": "2.20.1",
    "@medusajs/framework": "2.20.1",
    "@medusajs/medusa": "2.20.1",
    "@medusajs/ui": "4.2.3",
    "@tanstack/react-query": "5.64.2",
    "react-i18next": "13.5.0",
    "react-router-dom": "7.18.2",
    "zod": "4.2.0"
  },
  "devDependencies": {
    "@medusajs/test-utils": "2.20.1",
    "@swc/core": "^1.7.28",
    "@swc/jest": "^0.2.36",
    "@types/jest": "^29.5.13",
    "@types/node": "^20.12.11",
    "@types/react": "^18.3.2",
    "@types/react-dom": "^18.2.25",
    "jest": "^29.7.0",
    "prop-types": "^15.8.1",
    "react": "^18.3.1",
    "react-dom": "^18.3.1",
    "ts-node": "^10.9.2",
    "typescript": "^5.6.2",
    "vite": "^7.3.6",
    "yalc": "^1.0.0-pre.53"
  },
  "engines": {
    "node": "^20.19.0 || >=22.12.0"
  },
  "packageManager": "pnpm@10.28.2"
}

Node.js version

v24.20.0

Database and its version

PostgreSQL 16.15, official Alpine Docker image pinned by digest in the reproduction launcher.

Operating system name and version

Ubuntu 26.04.1 LTS, x86_64.

Browser name

Not applicable. Reproduced by invoking the native workflow through medusaIntegrationTestRunner.

What happended?

On Medusa 2.20.1, deleteProductVariantsWorkflow deletes a variant and its native price set, but leaves a linked custom record active despite deleteCascade: true.

The reproduction uses unmodified published packages and a small Brand module. Its link is defined using the standard descriptor:

export default defineLink(ProductModule.linkable.productVariant, {
  linkable: BrandModule.linkable.brand,
  deleteCascade: true,
})

Steps to reproduce:

  1. Create a product variant and a Brand record.
  2. Link them using product_variant_id and brand_id.
  3. Run deleteProductVariantsWorkflow(container).run({ input: { ids: [variant.id] } }).
  4. List the Brand record through its module service. It is still active.

The repository automates these steps with medusaIntegrationTestRunner and real PostgreSQL. With Node 24+, pnpm 10.28.2 and Docker:

git clone https://github.com/brunoocal/medusa-variant-link-repro.git
cd medusa-variant-link-repro
git checkout 5665d0263a4e28867f324a4f422027d70bdaf767
pnpm install --frozen-lockfile
pnpm reproduce

The command creates and removes its own disposable PostgreSQL container. No .env, existing database or Admin account is needed. Expected reproduction result: one passing test, one failing test, exit code 1.

Expected behavior

The workflow should soft-delete the linked custom record when deleting the variant, because the link enables deleteCascade: true.

Step 3 of the official digital products recipe uses this same variant descriptor and cascade option, and describes deleting the linked custom record when the variant is deleted.

Actual behavior

  • The variant is deleted.
  • Its native price set is deleted.
  • The linked Brand record remains active with deleted_at: null.

A separate control test calls link.delete({ product: { product_variant_id: variant.id } }). That correctly deletes the custom record, and link.restore restores it.

Both tests use real Medusa services and PostgreSQL. There are no internal mocks, retries or forceExit. The failure was also reproduced from a clean checkout. Build, strict typecheck and Medusa lint pass.

The workflow currently passes only variant_id to removeRemoteLinkStep, while the standard descriptor generates product_variant_id. This appears to explain the difference between the workflow and the direct Link.delete call. I can follow up with a regression test and a fix PR once the expected handling of these keys is confirmed.

This reproduction covers individual variant deletion through the workflow, not the HTTP endpoint, parent-product deletion or Redis providers. The captured output includes Node's experimental VM Modules warning and connection warnings from the test runner. Jest exits naturally with the failing assertion.

Link to reproduction repo

https://github.com/brunoocal/medusa-variant-link-repro

Reproduction commit: 5665d0263a4e28867f324a4f422027d70bdaf767.

Created with GPT-6 Astra in Codex.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions