Skip to content

fix(core-flows): cascade custom links when deleting product variants - #16733

Open
brunoocal wants to merge 3 commits into
medusajs:developfrom
brunoocal:fix/product-variant-link-cascade
Open

fix(core-flows): cascade custom links when deleting product variants#16733
brunoocal wants to merge 3 commits into
medusajs:developfrom
brunoocal:fix/product-variant-link-cascade

Conversation

@brunoocal

Copy link
Copy Markdown

Summary

What

Deleting a variant through deleteProductVariantsWorkflow now cascades to custom records linked with ProductModule.linkable.productVariant and deleteCascade: true.

Closes #16732.

Why

The standard descriptor generates product_variant_id, but the workflow only passed the legacy variant_id to removeRemoteLinkStep. Variants and native price sets were deleted while custom linked records remained active. The issue includes a standalone reproduction using unmodified 2.20.1 packages.

How

Pass both variant_id and product_variant_id in the existing step call. Keeping the legacy key preserves native price and inventory links. The existing compensation receives both keys and restores both link types. No link schema changes or changes to the general Link service are needed.

Testing

Four integration cases use real Medusa services and a disposable PostgreSQL instance:

  • Direct Link.delete and Link.restore work with the standard custom link.
  • Native variant deletion removes the custom record and native price set, while preserving another variant and its linked records.
  • A downstream failure restores the variant, custom record, native price set and both links. The test first verifies that the custom record was actually deleted before compensation.
  • Inventory exclusively owned by the deleted variant is still deleted.

Before the fix: 2 passed, 2 failed. With the fix: 4 passed, exit code 0. Jest exited naturally without forceExit or retries. No internal modules are mocked.

The core-flows build, strict typechecking of the new test and fixture, and targeted ESLint checks passed. The complete monorepo test suite was not run.

With the monorepo dependencies built and DB_HOST=localhost, DB_PORT, DB_USERNAME and DB_PASSWORD configured for a disposable PostgreSQL instance:

cd integration-tests/modules
node ../../node_modules/jest/bin/jest.js --config ./jest.config.js --runInBand --runTestsByPath __tests__/link-modules/product-variant-cascade.spec.ts

Example

This existing standard link now cascades when the native variant-deletion workflow runs:

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

Checklist

  • Added a patch changeset for @medusajs/core-flows.
  • Added regression and compatibility tests.
  • Verified behavior locally.
  • Linked the related issue.

Additional context

This change covers individual variant deletion. Parent-product deletion uses another workflow and is not changed here. The tests exercise the workflow directly, not the HTTP endpoint or separate server/worker processes.

The test runner prints Connection Error: Connection ended unexpectedly both before and after the fix. These warnings were not suppressed, and all four patched tests complete with exit code 0.

Created with GPT-6 Astra in Codex.

Bruno added 2 commits September 4, 2026 22:12
Process product_variant_id alongside legacy variant_id when deleting variants. The existing compensation restores both link types. Verified with four PostgreSQL integration cases, strict fixture typechecking and targeted lint.
@brunoocal
brunoocal requested a review from a team as a code owner September 5, 2026 02:02
@changeset-bot

changeset-bot Bot commented Sep 5, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: b367b15

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 83 packages
Name Type
@medusajs/core-flows Patch
@medusajs/medusa Patch
@medusajs/test-utils Patch
integration-tests-http Patch
@medusajs/loyalty-plugin Patch
@medusajs/medusa-oas-cli Patch
@medusajs/analytics Patch
@medusajs/api-key Patch
@medusajs/auth Patch
@medusajs/caching Patch
@medusajs/cart Patch
@medusajs/currency Patch
@medusajs/customer Patch
@medusajs/file Patch
@medusajs/fulfillment Patch
@medusajs/index Patch
@medusajs/inventory Patch
@medusajs/link-modules Patch
@medusajs/locking Patch
@medusajs/notification Patch
@medusajs/order Patch
@medusajs/payment Patch
@medusajs/pricing Patch
@medusajs/product Patch
@medusajs/promotion Patch
@medusajs/rbac Patch
@medusajs/region Patch
@medusajs/sales-channel Patch
@medusajs/search Patch
@medusajs/settings Patch
@medusajs/stock-location Patch
@medusajs/store Patch
@medusajs/tax Patch
@medusajs/translation Patch
@medusajs/user Patch
@medusajs/workflow-engine-inmemory Patch
@medusajs/workflow-engine-redis Patch
@medusajs/search-postgres Patch
@medusajs/draft-order Patch
@medusajs/oas-github-ci Patch
@medusajs/cache-inmemory Patch
@medusajs/cache-redis Patch
@medusajs/event-bus-local Patch
@medusajs/event-bus-redis Patch
@medusajs/analytics-local Patch
@medusajs/analytics-posthog Patch
@medusajs/auth-emailpass Patch
@medusajs/auth-github Patch
@medusajs/auth-google Patch
@medusajs/auth-oidc Patch
@medusajs/caching-redis Patch
@medusajs/file-local Patch
@medusajs/file-s3 Patch
@medusajs/fulfillment-manual Patch
@medusajs/locking-postgres Patch
@medusajs/locking-redis Patch
@medusajs/notification-local Patch
@medusajs/notification-sendgrid Patch
@medusajs/payment-stripe Patch
@medusajs/framework Patch
@medusajs/instantsearch-adapter Patch
@medusajs/js-sdk Patch
@medusajs/modules-sdk Patch
@medusajs/orchestration Patch
@medusajs/query Patch
@medusajs/types Patch
@medusajs/utils Patch
@medusajs/workflows-sdk Patch
create-medusa-app Patch
@medusajs/http-types-generator Patch
@medusajs/cli Patch
@medusajs/deps Patch
@medusajs/eslint-plugin Patch
@medusajs/telemetry Patch
@medusajs/admin-bundler Patch
@medusajs/admin-sdk Patch
@medusajs/admin-shared Patch
@medusajs/admin-vite-plugin Patch
@medusajs/dashboard Patch
@medusajs/icons Patch
@medusajs/toolbox Patch
@medusajs/ui-preset Patch
@medusajs/ui Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@medusa-os-bot

medusa-os-bot Bot commented Sep 5, 2026

Copy link
Copy Markdown

Thanks for the contribution! A few items need to be addressed before this can move forward:

The contributor targets a real, confirmed bug where deleteProductVariantsWorkflow only passed the legacy variant_id key to removeRemoteLinkStep, leaving custom links registered with ProductModule.linkable.productVariant (which generate a product_variant_id key) un-deleted. The fix passes both keys in a single call, which is correct given how removeRemoteLinkStep groups and forwards them to link.delete. The compensation path in removeRemoteLinkStep receives the full grouped object and so will restore both key types, which is also correct. The integration tests are comprehensive: they exercise direct link delete/restore, cascade deletion via the workflow (including the native price-set link), compensation after a downstream failure, and inventory-only deletion. Tests are placed in integration-tests/modules/ following existing patterns and use the pre-existing brand module fixture. PR template is complete, a patch changeset targeting @medusajs/core-flows is included, and the fix is narrowly scoped to the stated issue. The linked issue (#16732) carries the help-wanted label. One required change: the changeset message body does not follow Medusa's required format.

  • .changeset/fix-variant-custom-link-cascade.md: The changeset message must start with fix(@medusajs/core-flows): followed by a short description. The current body is a plain sentence without the required prefix. Update it to, for example: fix(@medusajs/core-flows): cascade custom links on product-variant deletion.

Triggered by: new PR opened

@medusa-os-bot

medusa-os-bot Bot commented Sep 5, 2026

Copy link
Copy Markdown

Thanks for the contribution! Initial automated review looks good.

The contributor fixes a real bug where deleteProductVariantsWorkflow only passed the legacy variant_id key to removeRemoteLinkStep, leaving custom links registered via ProductModule.linkable.productVariant (which generate a product_variant_id key) un-deleted on variant deletion. The fix adds product_variant_id alongside variant_id in the single removeRemoteLinkStep call; both keys are forwarded by the step and its compensation, so native links (price set, inventory) and descriptor-generated custom links are both handled correctly. The previously flagged changeset format issue has been resolved: the message now reads fix(@medusajs/core-flows): cascade custom links on product-variant deletion, which follows the required format. The four integration tests are comprehensive — they cover direct link delete/restore, cascade deletion via the workflow (including the native price-set link), compensation after a downstream step failure, and exclusive-inventory deletion. Tests are placed in integration-tests/modules/ following existing patterns and use the shared brand module fixture. The changeset targets @medusajs/core-flows as a patch, a linked and confirmed issue is present, and the diff is narrowly scoped to the stated problem.

Triggered by: new commit pushed

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

1 participant