-
-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Description
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
- 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
}
)- Call
createProductsWorkflowfrom a migration script insrc/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 */ }
}
})
}- 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:
/packages/medusa/src/commands/db/run-scripts.ts(line 107) usesMedusaAppLoaderto load resourcesMedusaAppLoaderdoes NOT load workflow files from the app directory/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:migratehas 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