{"record":{"id":"4ceba1d304f3aa6e","repo":"medusajs/medusa","slug":"step-registration-requires-either-sourcepath-or-wo","errorCode":null,"errorMessage":"Step registration requires either sourcePath or workflowId. Received: ${JSON.stringify(data)}","messagePattern":"Step registration requires either sourcePath or workflowId\\. Received: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/core/utils/src/dev-server/handlers/step-handler.ts","lineNumber":14,"sourceCode":"import { ResourceEntry, ResourceTypeHandler, StepResourceData } from \"../types\"\n\nexport class StepHandler implements ResourceTypeHandler<StepResourceData> {\n  readonly type = \"step\"\n\n  constructor(private inverseRegistry: Map<string, string[]>) {}\n\n  validate(data: StepResourceData): void {\n    if (!data.id) {\n      throw new Error(\n        `Step registration requires id. Received: ${JSON.stringify(data)}`\n      )\n    }\n\n    if (!data.sourcePath && !data.workflowId) {\n      throw new Error(\n        `Step registration requires either sourcePath or workflowId. Received: ${JSON.stringify(\n          data\n        )}`\n      )\n    }\n  }\n\n  resolveSourcePath(data: StepResourceData): string {\n    if (data.sourcePath) {\n      return data.sourcePath\n    }\n\n    // Look up workflow's source path\n    const workflowKey = `workflow:${data.workflowId}`\n    const workflowSourcePaths = this.inverseRegistry.get(workflowKey)\n","sourceCodeStart":1,"sourceCodeEnd":32,"githubUrl":"https://github.com/medusajs/medusa/blob/5e06e544a296b9033f20f71f11c559f81a0e5739/packages/core/utils/src/dev-server/handlers/step-handler.ts#L1-L32","documentation":"Dev-server validation error from StepHandler.validate: a step resource was registered with neither `sourcePath` nor `workflowId`. The dev server resolves a step's file either directly from its source path or by looking up the parent workflow's registered source; if both are absent it cannot map the step to a file, so registration is rejected.","triggerScenarios":"Registering a step with only { type: 'step', id } and nothing else; registration data built from a partial object where both fields were dropped during refactoring.","commonSituations":"Custom loaders registering steps they discovered without file context; splitting step definitions into helpers imported by workflows so the loader loses track of both the file and the owning workflow.","solutions":["Provide sourcePath (file path of the step module) — the direct option.","Or provide workflowId of an already-registered workflow so the handler can resolve the step's path from the inverse registry.","If the step belongs to a workflow, make sure the workflow itself is registered before the step."],"exampleFix":"// before\nregisterDevServerResource({ type: 'step', id: 'price-step' })\n\n// after\nregisterDevServerResource({\n  type: 'step',\n  id: 'price-step',\n  sourcePath: fileURLToPath(import.meta.url),\n})","handlingStrategy":"validation","validationCode":"if (!data.sourcePath && !data.workflowId) throw new Error(`Step ${data.id} needs sourcePath or workflowId`)","typeGuard":"function isResolvableStep(d: { sourcePath?: string; workflowId?: string }): boolean {\n  return Boolean(d.sourcePath) || Boolean(d.workflowId)\n}","tryCatchPattern":null,"preventionTips":["Prefer registering steps with an explicit sourcePath for independence.","When relying on workflowId, ensure the workflow registration happens first.","Build registration data next to the step definition so paths are always in scope."],"tags":["dev-server","step","source-path","workflow","registration","validation"],"backgroundTag":"missing-required-field-registration","analyzedSha":"5e06e544a296b9033f20f71f11c559f81a0e5739","analyzedAt":"2026-08-27T07:24:39.599Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}