{"record":{"id":"54f9148419b123ab","repo":"immich-app/immich","slug":"unable-to-infer-workflow-event-type-from-steps","errorCode":null,"errorMessage":"Unable to infer workflow event type from steps","messagePattern":"Unable to infer workflow event type from steps","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"server/src/services/workflow-execution.service.ts","lineNumber":408,"sourceCode":"    getHandler: (type: T) => ExecuteOptions<T> | undefined,\n  ) {\n    const workflow = await this.workflowRepository.getForWorkflowRun(workflowId);\n    if (!workflow) {\n      return;\n    }\n\n    // TODO infer from steps\n    let type: T | undefined;\n    for (const targetType of Object.values(WorkflowType)) {\n      const isMissing = workflow.steps.some((step) => !step.types.includes(targetType));\n      if (!isMissing) {\n        type = targetType as unknown as T;\n        break;\n      }\n    }\n\n    if (!type) {\n      throw new Error('Unable to infer workflow event type from steps');\n    }\n\n    const handler = getHandler(type);\n    if (!handler) {\n      this.logger.error(`Misconfigured workflow ${workflowId}: no handler for type ${type}`);\n      return;\n    }\n\n    const { read, write } = handler;\n    const readResult = await read(type);\n    let data = readResult.data;\n    const runId = crypto.randomUUID();\n\n    for (const step of workflow.steps) {\n      try {\n        const payload: WorkflowEventPayload<typeof type> = {\n          trigger: workflow.trigger,\n          type,","sourceCodeStart":390,"sourceCodeEnd":426,"githubUrl":"https://github.com/immich-app/immich/blob/e55ac299a4ec7cb372e35dbf2c6c05ee9ce77f6c/server/src/services/workflow-execution.service.ts#L390-L426","documentation":"execute cannot determine which WorkflowType a workflow's steps correspond to: after iterating the steps and matching them against known target types, no type could be inferred. This is an internal invariant violation indicating misconfigured workflow data — steps that map to no known event type.","triggerScenarios":"handleAssetTrigger invokes execute for a workflow whose stored steps array is empty or whose steps' methods resolve to no recognized WorkflowType, so the `type` variable remains undefined after the matching loop.","commonSituations":"Workflow definitions saved by an older version whose step methods were renamed/removed; hand-edited or imported workflow JSON with unknown step payloads; a workflow created with zero steps; plugin methods removed from the registry while workflows still reference them.","solutions":["Inspect the workflow's steps in the database/definition and verify each step's method maps to a known workflow type.","Delete or re-create the broken workflow via the workflow editor so steps are re-validated against current methods.","Update Immich (or the workflow definitions) if a version migration renamed step methods; run any pending migrations.","If programmatically creating workflows, always run them through resolveAndValidateSteps before persisting so bad steps are rejected early."],"exampleFix":"// before\nconst steps = workflow.steps; // possibly stale/unvalidated\nawait execute(workflowId, steps);\n// after\nconst steps = await resolveAndValidateSteps(workflow.trigger, workflow.steps);\nif (steps.length === 0) return; // skip empty/invalid workflows\nawait execute(workflowId, steps);","handlingStrategy":"validation","validationCode":"const inferType = (steps: Step[]) =>\n  steps.map((s) => TARGET_TYPES.find((t) => matches(s, t))).find(Boolean);\nif (!steps.length || !inferType(steps)) {\n  logger.warn('skipping workflow with uninferrable steps');\n  return;\n}","typeGuard":"const hasKnownType = (steps: Step[]): steps is Step[] & { length: number } =>\n  steps.length > 0 && steps.every((s) => KNOWN_TYPES.includes(s.type as T));","tryCatchPattern":"try {\n  await execute(workflowId, steps);\n} catch (e) {\n  if (e.message === 'Unable to infer workflow event type from steps') {\n    logger.error(`workflow ${workflowId} has unrecognizable steps; skipping`);\n    return;\n  }\n  throw e;\n}","preventionTips":["Validate steps through resolveAndValidateSteps before persisting workflows.","Run migrations when upgrading so step methods are renamed consistently.","Reject workflow definitions with zero steps at creation time.","Re-validate imported workflows against the current method registry."],"tags":["workflow","inference","invariant","misconfiguration"],"backgroundTag":"internal-invariant-violation","analyzedSha":"e55ac299a4ec7cb372e35dbf2c6c05ee9ce77f6c","analyzedAt":"2026-09-15T07:20:19.675Z","contentChangedAt":"2026-09-15T07:20:19.675Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}