Skip to content

Workflow hooks from main app directory are not loaded during migrations #14125

@erickirt

Description

@erickirt

Bug Report

Description

Workflow hooks defined in the main application directory () are not loaded during execution, causing hook handlers to never be registered. This results in hooks silently failing to execute even though the workflows themselves run successfully.

Steps to Reproduce

  1. Create a workflow hook in your app at src/workflows/hooks/my-hook.ts:
import { createProductsWorkflow } from "@medusajs/medusa/core-flows"

createProductsWorkflow.hooks.productsCreated(
  async ({ products, additional_data }, { container }) => {
    console.log("Hook executed!")
    // Custom logic here
  }
)
  1. Call createProductsWorkflow from a migration script in src/migration-scripts/:
import { createProductsWorkflow } from "@medusajs/medusa/core-flows"

export default async function seedData({ container }: ExecArgs) {
  await createProductsWorkflow(container).run({
    input: {
      products: [...],
      additional_data: { /* custom data */ }
    }
  })
}
  1. Run medusa db:migrate

Expected Behavior

The workflow hook handler should be loaded and executed when the workflow runs, just like it does in integration tests or when the dev server is running.

Actual Behavior

  • The workflow executes successfully
  • Products are created
  • The hook handler never executes (no console.log appears)
  • No errors are thrown

Root Cause

The issue is in the migration execution flow:

  1. /packages/medusa/src/commands/db/run-scripts.ts (line 107) uses MedusaAppLoader to load resources
  2. MedusaAppLoader does NOT load workflow files from the app directory
  3. /packages/medusa/src/loaders/index.ts (lines 199-201) only loads workflows from plugins:
const workflowsSourcePaths = plugins.map((p) => join(p.resolve, "workflows"))
const workflowLoader = new WorkflowLoader(workflowsSourcePaths, container)
await workflowLoader.load()

Compare this to how subscribers and jobs are loaded (which DO include the app directory):

// Subscribers include app directory
const subscribersSourcePaths = [join(__dirname, "../subscribers"), ...plugins.map(...)]

// Jobs include app directory  
const jobsSourcePaths = [join(__dirname, "../jobs"), ...plugins.map(...)]

// But workflows only include plugins
const workflowsSourcePaths = plugins.map((p) => join(p.resolve, "workflows"))

Workaround

Explicitly import the hook file in the migration script:

import { createProductsWorkflow } from "@medusajs/medusa/core-flows"
// Force load the hook
import "../workflows/hooks/my-hook"

export default async function seedData({ container }: ExecArgs) {
  // Hook is now registered and will execute
  await createProductsWorkflow(container).run({ ... })
}

Proposed Solution

Update the workflow loader paths in /packages/medusa/src/loaders/index.ts to include the main app directory, similar to subscribers and jobs:

const workflowsSourcePaths = [
  join(__dirname, "../workflows"),  // Add app workflows
  ...plugins.map((p) => join(p.resolve, "workflows"))
]
const workflowLoader = new WorkflowLoader(workflowsSourcePaths, container)
await workflowLoader.load()

This would ensure workflow hooks are consistently loaded across all execution contexts (dev server, integration tests, AND migrations).

Environment

  • Medusa Version: 2.11.2
  • Node Version: 22.x
  • Database: PostgreSQL

Additional Context

  • Integration tests work correctly because they load the full app
  • The dev server works correctly because it loads workflows from the app directory
  • Only medusa db:migrate has this issue
  • This affects any custom workflow hooks, not just createProductsWorkflow

Related Files

  • /packages/medusa/src/commands/db/run-scripts.ts
  • /packages/medusa/src/loaders/index.ts
  • /packages/core/framework/src/medusa-app-loader.ts
  • /packages/core/framework/src/workflows/workflow-loader.ts

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions