{"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":393,"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    try {\n      const { read, write } = handler;\n      const readResult = await read(type);\n      let data = readResult.data;\n      for (const step of workflow.steps) {\n        const payload: WorkflowEventPayload<typeof type> = {\n          trigger: workflow.trigger,\n          type,\n          config: step.config ?? {},\n          workflow: {","sourceCodeStart":375,"sourceCodeEnd":411,"githubUrl":"https://github.com/immich-app/immich/blob/199723261c6ffa897fec8ccdaea6359e39c37cc3/server/src/services/workflow-execution.service.ts#L375-L411","documentation":"A plain Error thrown by WorkflowExecutionService.execute when no single WorkflowType is common to all workflow steps. The inference loop checks each WorkflowType value and asks whether any step lacks that type in its step.types; if none of the WorkflowType values are present in every step, the workflow cannot be run and the job fails.","triggerScenarios":"A workflow was created or updated with steps whose declared types do not intersect (e.g., one step supports AssetV1 and another supports a future AssetPersonV1 that AssetV1 does not cover). The TODO comment notes inference is temporary, so misconfigured combinations reach this branch.","commonSituations":"Mixing plugin methods that declare incompatible WorkflowType sets; plugin upgraded to declare new types that no longer overlap; manually editing workflow steps in the DB to add an incompatible method.","solutions":["Edit the workflow so every step's method declares at least one WorkflowType in common with the others (today, typically AssetV1).","Replace or remove the offending step whose types do not include the common type.","Re-validate the workflow via the create/update endpoint which runs resolveAndValidateSteps before persistence.","After fixing, re-trigger the workflow run."],"exampleFix":"// before: step A types=[AssetV1], step B types=[AssetPersonV1] -> no common type\n// after: replace step B with a method whose types include AssetV1","handlingStrategy":"validation","validationCode":"function findCommonWorkflowType(steps) {\n  const allTypes = Object.values(WorkflowType);\n  return allTypes.find((t) => steps.every((s) => s.types.includes(t)));\n}\nconst common = findCommonWorkflowType(steps);\nif (!common) {\n  return { ok: false, reason: 'No common WorkflowType across steps' };\n}","typeGuard":"const isWorkflowTypeInferenceError = (e: unknown): boolean =>\n  typeof e === 'object' && e !== null && typeof (e as any).message === 'string' &&\n  (e as any).message.includes('Unable to infer workflow event type');","tryCatchPattern":"// This error fails the workflow job; catch at the runner boundary\ntry {\n  await runWorkflow(workflowId);\n} catch (e) {\n  if (isWorkflowTypeInferenceError(e)) {\n    notifyAdminToFixWorkflowSteps(workflowId);\n  }\n}","preventionTips":["Use create/update workflow endpoints which validate steps before persistence.","Keep all steps declaring a common type (AssetV1 today).","Re-validate workflows after plugin upgrades that change declared types."],"tags":["workflow","configuration","workflow-type","inference"],"backgroundTag":null,"analyzedSha":"199723261c6ffa897fec8ccdaea6359e39c37cc3","analyzedAt":"2026-08-12T04:54:27.085Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}