{"record":{"id":"5040c26639524894","repo":"immich-app/immich","slug":"method-step-method-is-incompatible-with-workf","errorCode":null,"errorMessage":"Method \"${step.method}\" is incompatible with workflow trigger: \"${trigger}\"","messagePattern":"Method \"(.+?)\" is incompatible with workflow trigger: \"(.+?)\"","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"server/src/services/workflow.service.ts","lineNumber":97,"sourceCode":"  }\n\n  async delete(auth: AuthDto, id: string): Promise<void> {\n    await this.requireAccess({ auth, permission: Permission.WorkflowDelete, ids: [id] });\n    await this.workflowRepository.delete(id);\n  }\n\n  private async resolveAndValidateSteps<T extends { method: string }>(steps: T[], trigger: WorkflowTrigger) {\n    const methods = await this.pluginRepository.getForValidation();\n    const results: Array<T & { pluginMethod: PluginMethodSearchResponse }> = [];\n\n    for (const step of steps) {\n      const pluginMethod = resolveMethod(methods, step.method);\n      if (!pluginMethod) {\n        throw new BadRequestException(`Unknown method ${step.method}`);\n      }\n\n      if (!isMethodCompatible(pluginMethod, trigger)) {\n        throw new BadRequestException(`Method \"${step.method}\" is incompatible with workflow trigger: \"${trigger}\"`);\n      }\n\n      results.push({ ...step, pluginMethod });\n    }\n\n    // TODO make sure all steps can use a common WorkflowType\n\n    return results;\n  }\n\n  private findOrFail(id: string) {\n    return findOrFail(() => this.workflowRepository.get(id), 'Workflow');\n  }\n}\n","sourceCodeStart":79,"sourceCodeEnd":112,"githubUrl":"https://github.com/immich-app/immich/blob/199723261c6ffa897fec8ccdaea6359e39c37cc3/server/src/services/workflow.service.ts#L79-L112","documentation":"A BadRequestException (HTTP 400) thrown by WorkflowService.resolveAndValidateSteps when the resolved plugin method's declared WorkflowType set is not compatible with the workflow's trigger. isMethodCompatible checks whether any of the trigger's valid types (from triggerMap) appear in the method's type set (or its inferred supertypes); if not, the method cannot handle that trigger.","triggerScenarios":"POST/PUT /workflows with a trigger of AssetCreate (valid types: [AssetV1]) but a step method that only declares a non-overlapping type such as a future AssetPersonV1. The method resolves but its types do not cover the trigger's required type.","commonSituations":"Plugin author declares a method for a type not yet wired into triggerMap; trigger changed on an existing workflow to one the method does not support; plugin upgraded and dropped a type from a method.","solutions":["Choose a method whose declared types include the WorkflowType associated with the trigger (AssetV1 for AssetCreate / AssetMetadataExtraction).","Change the workflow trigger to one the method supports.","Ask the plugin author to add the missing WorkflowType to the method's declaration.","Validate trigger/method compatibility client-side using the trigger list and method search results."],"exampleFix":"// before: trigger=AssetCreate, method only declares AssetPersonV1\n{ \"trigger\": \"AssetCreate\", \"steps\": [{ \"method\": \"p#onPerson\" }] }\n\n// after: use a method that declares AssetV1\n{ \"trigger\": \"AssetCreate\", \"steps\": [{ \"method\": \"p#onAsset\" }] }","handlingStrategy":"validation","validationCode":"function isStepCompatibleWithTrigger(method, trigger) {\n  // mirror server's isMethodCompatible using triggerMap from /workflows/triggers\n  const validTypes = triggerMap[trigger];\n  return method.types.some((t) => validTypes.includes(t));\n}\nfor (const step of steps) {\n  if (!isStepCompatibleWithTrigger(step.pluginMethod, trigger)) {\n    return badRequest(`Method ${step.method} incompatible with ${trigger}`);\n  }\n}","typeGuard":"const isIncompatibleTriggerError = (e: unknown): boolean =>\n  typeof e === 'object' && e !== null && (e as any).status === 400 &&\n  typeof (e as any).message === 'string' && (e as any).message.includes('incompatible with workflow trigger');","tryCatchPattern":"try {\n  await api.createWorkflow(dto);\n} catch (e) {\n  if (isIncompatibleTriggerError(e)) {\n    setFieldError('trigger', e.message);\n    return;\n  }\n  throw e;\n}","preventionTips":["Fetch triggers and method type sets together so the UI can filter compatible methods per trigger.","When changing a workflow trigger, re-validate all existing steps.","After plugin upgrades, re-check that workflow methods still declare the required types."],"tags":["workflow","plugin","validation","trigger","nestjs"],"backgroundTag":null,"analyzedSha":"199723261c6ffa897fec8ccdaea6359e39c37cc3","analyzedAt":"2026-08-12T04:54:27.085Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}