{"record":{"id":"0da45073dcaf609a","repo":"windmill-labs/windmill","slug":"unexpected-trigger-kind-triggerkind-allowed-ki","errorCode":null,"errorMessage":"Unexpected trigger kind ${triggerKind}. Allowed kinds are: routes, kafka, mqtt, postgres, sqs, gcp, websockets, nats, azure, emails, schedules.","messagePattern":"Unexpected trigger kind (.+?)\\. Allowed kinds are: routes, kafka, mqtt, postgres, sqs, gcp, websockets, nats, azure, emails, schedules\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"frontend/src/lib/utils_deployable.ts","lineNumber":119,"sourceCode":"\t} else if (triggerKind === 'postgres') {\n\t\treturn await PostgresTriggerService.existsPostgresTrigger(data)\n\t} else if (triggerKind === 'sqs') {\n\t\treturn await SqsTriggerService.existsSqsTrigger(data)\n\t} else if (triggerKind === 'gcp') {\n\t\treturn await GcpTriggerService.existsGcpTrigger(data)\n\t} else if (triggerKind === 'websockets') {\n\t\treturn await WebsocketTriggerService.existsWebsocketTrigger(data)\n\t} else if (triggerKind === 'nats') {\n\t\treturn await NatsTriggerService.existsNatsTrigger(data)\n\t} else if (triggerKind === 'azure') {\n\t\treturn await AzureTriggerService.existsAzureTrigger(data)\n\t} else if (triggerKind === 'emails') {\n\t\treturn await EmailTriggerService.existsEmailTrigger(data)\n\t} else if (triggerKind === 'schedules') {\n\t\treturn await ScheduleService.existsSchedule(data)\n\t}\n\n\tthrow new Error(\n\t\t`Unexpected trigger kind ${triggerKind}. Allowed kinds are: routes, kafka, mqtt, postgres, sqs, gcp, websockets, nats, azure, emails, schedules.`\n\t)\n}\n\n/**\n * Strip operational state (`mode`, `enabled`) from a trigger/schedule payload\n * before sending it to an update endpoint via the merge UI. The backend's\n * `update_trigger` handler preserves the target row's existing `mode` when\n * both fields are absent from the request (`is_mode_unspecified()`), so\n * stripping here lets a fork→parent (or parent→fork) deploy carry config\n * changes without flipping the target's enabled/disabled state. Schedules'\n * `EditSchedule` already lacks `enabled` on the backend, but stripping keeps\n * the intent explicit and matches the YAML/CLI round-trip behavior.\n *\n * Used by the legacy `kind === 'trigger'` path in `utils_workspace_deploy.ts`\n * (the cross-workspace deploy UI). The merge-UI deploy goes through the\n * shared `deployItem` in `windmill-utils-internal`, which applies its own\n * `stripOperationalStateOnUpdate` at the dispatch layer.","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/frontend/src/lib/utils_deployable.ts#L101-L137","documentation":"existsTrigger in frontend/src/lib/utils_deployable.ts (line 119) is the central dispatcher that maps a TriggerKind string to the correct `*Service.exists*Trigger` API call. It only recognizes the 12 kinds that map to backend trigger endpoints (routes, kafka, mqtt, amqp, postgres, sqs, gcp, websockets, nats, azure, emails, schedules); the TriggerKind union in frontend/src/lib/components/triggers.ts:44 also contains UI-only kinds ('webhooks', 'default_emails', 'cli', 'scheduledPoll', 'nextcloud', 'google', 'github') which fall through to this throw. Note the message's allowed-kinds list is slightly stale: it omits 'amqp', which the code does handle.","triggerScenarios":"Calling existsTrigger(data, triggerKind) with any TriggerKind not in the if-chain — concretely 'webhooks', 'default_emails', 'cli', 'scheduledPoll', 'nextcloud', 'google', or 'github' — or a typo'd/renamed kind like 'schedule' instead of 'schedules' or 'http' instead of 'routes'. Called from alreadyExists and checkItemExists during workspace deploy checks.","commonSituations":"A developer adds a new trigger type (or UI-only trigger kind) to the TriggerKind union and to a page/feature that lists triggers, but forgets to add a branch in existsTrigger; code that was typed against a narrower kind union is fed a kind coming from the backend's compareWorkspaces output or a user-supplied query param; renaming a kind string in one module but not the dispatcher.","solutions":["Check the actual triggerKind value logged in the error; it must be one of routes, kafka, mqtt, amqp, postgres, sqs, gcp, websockets, nats, azure, emails, schedules — fix the caller to pass one of those (e.g. 'schedules' not 'schedule', 'routes' not 'http').","If the kind is a UI-only kind ('webhooks', 'cli', 'scheduledPoll', 'nextcloud', 'google', 'github', 'default_emails'), do not route it through existsTrigger; handle it before the call or add an exists check branch mapping it to the right service.","If you added a new trigger kind, add a matching `else if` branch in existsTrigger wiring it to its Service's exists endpoint, and update the allowed-kinds list in the error message.","If the kind arrives from the backend compareWorkspaces output (e.g. 'http_trigger', 'kafka_trigger'), convert it with the existing kind-mapping helpers before calling existsTrigger."],"exampleFix":"// before\nawait existsTrigger({ workspace, path }, 'schedule')\n// after\nawait existsTrigger({ workspace, path }, 'schedules')","handlingStrategy":"validation","validationCode":"const EXISTS_KINDS = ['routes','kafka','mqtt','amqp','postgres','sqs','gcp','websockets','nats','azure','emails','schedules'] as const\nif (!EXISTS_KINDS.includes(triggerKind as any)) {\n  throw new Error(`Skipping exists check: unsupported trigger kind ${triggerKind}`)\n}\nawait existsTrigger({ workspace, path }, triggerKind)","typeGuard":"const EXISTS_KINDS = ['routes','kafka','mqtt','amqp','postgres','sqs','gcp','websockets','nats','azure','emails','schedules'] as const\ntype ExistsKind = (typeof EXISTS_KINDS)[number]\nfunction isExistsKind(k: string): k is ExistsKind {\n  return (EXISTS_KINDS as readonly string[]).includes(k)\n}","tryCatchPattern":"try {\n  const exists = await existsTrigger({ workspace, path }, triggerKind)\n} catch (e) {\n  if (String(e?.message).startsWith('Unexpected trigger kind')) {\n    console.warn(`No exists-check for kind ${triggerKind}; treating as not-existing`)\n  } else throw e\n}","preventionTips":["Only pass kinds from the backend capture/compare APIs through the dispatchers; convert per-kind names ('http_trigger'→'routes') at the boundary.","When extending the TriggerKind union, grep for existsTrigger/getTriggersDeployData/getTriggerValue/getTriggerDependency and update all of them together.","Treat the error message's allowed-kinds list with suspicion — verify against the if-chain (e.g. 'amqp' is handled but missing from the list)."],"tags":["frontend","typescript","trigger","dispatch","unsupported-kind"],"backgroundTag":"unsupported-trigger-kind","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}