windmill-labs/windmill · error · Error
Unexpected trigger kind ${triggerKind}. Allowed kinds are: r
Error message
Unexpected trigger kind ${triggerKind}. Allowed kinds are: routes, kafka, mqtt, postgres, sqs, gcp, websockets, nats, azure, emails, schedules. What it means
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.
Source
Thrown at frontend/src/lib/utils_deployable.ts:119
} else if (triggerKind === 'postgres') {
return await PostgresTriggerService.existsPostgresTrigger(data)
} else if (triggerKind === 'sqs') {
return await SqsTriggerService.existsSqsTrigger(data)
} else if (triggerKind === 'gcp') {
return await GcpTriggerService.existsGcpTrigger(data)
} else if (triggerKind === 'websockets') {
return await WebsocketTriggerService.existsWebsocketTrigger(data)
} else if (triggerKind === 'nats') {
return await NatsTriggerService.existsNatsTrigger(data)
} else if (triggerKind === 'azure') {
return await AzureTriggerService.existsAzureTrigger(data)
} else if (triggerKind === 'emails') {
return await EmailTriggerService.existsEmailTrigger(data)
} else if (triggerKind === 'schedules') {
return await ScheduleService.existsSchedule(data)
}
throw new Error(
`Unexpected trigger kind ${triggerKind}. Allowed kinds are: routes, kafka, mqtt, postgres, sqs, gcp, websockets, nats, azure, emails, schedules.`
)
}
/**
* Strip operational state (`mode`, `enabled`) from a trigger/schedule payload
* before sending it to an update endpoint via the merge UI. The backend's
* `update_trigger` handler preserves the target row's existing `mode` when
* both fields are absent from the request (`is_mode_unspecified()`), so
* stripping here lets a fork→parent (or parent→fork) deploy carry config
* changes without flipping the target's enabled/disabled state. Schedules'
* `EditSchedule` already lacks `enabled` on the backend, but stripping keeps
* the intent explicit and matches the YAML/CLI round-trip behavior.
*
* Used by the legacy `kind === 'trigger'` path in `utils_workspace_deploy.ts`
* (the cross-workspace deploy UI). The merge-UI deploy goes through the
* shared `deployItem` in `windmill-utils-internal`, which applies its own
* `stripOperationalStateOnUpdate` at the dispatch layer.View on GitHub (pinned to e474e8803c)
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.
Example fix
// before
await existsTrigger({ workspace, path }, 'schedule')
// after
await existsTrigger({ workspace, path }, 'schedules') Defensive patterns
Strategy: validation
Validate before calling
const EXISTS_KINDS = ['routes','kafka','mqtt','amqp','postgres','sqs','gcp','websockets','nats','azure','emails','schedules'] as const
if (!EXISTS_KINDS.includes(triggerKind as any)) {
throw new Error(`Skipping exists check: unsupported trigger kind ${triggerKind}`)
}
await existsTrigger({ workspace, path }, triggerKind) Type guard
const EXISTS_KINDS = ['routes','kafka','mqtt','amqp','postgres','sqs','gcp','websockets','nats','azure','emails','schedules'] as const
type ExistsKind = (typeof EXISTS_KINDS)[number]
function isExistsKind(k: string): k is ExistsKind {
return (EXISTS_KINDS as readonly string[]).includes(k)
} Try / catch
try {
const exists = await existsTrigger({ workspace, path }, triggerKind)
} catch (e) {
if (String(e?.message).startsWith('Unexpected trigger kind')) {
console.warn(`No exists-check for kind ${triggerKind}; treating as not-existing`)
} else throw e
} Prevention
- 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).
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Unexpected trigger kind got: ${kind}
- Draft diff not supported for kind ${kind}
- --kind is required. Valid kinds: ${TRIGGER_TYPES.join(", ")}
- Invalid trigger kind: ${opts.kind}. Valid kinds: ${TRIGGER_T
- File already exists: ${filePath}
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/0da45073dcaf609a.
Report an issue: GitHub.