windmill-labs/windmill · error
Unsupported draft type "${type}".
Error message
Unsupported draft type "${type}". What it means
persistGlobalDraft() is the chat's write path for user drafts (scripts, flows, apps, resources, variables, schedules, triggers). Before persisting it maps the chat-facing WorkspaceItemType (plus optional triggerKind) to an internal UserDraftItemKind via itemKindFor(); types that have no draft representation — or 'trigger' without a triggerKind — return undefined and this error is thrown. It is a fail-fast guard so an unmappable type never lands in the draft store.
Source
Thrown at frontend/src/lib/components/copilot/chat/global/userDraftAdapter.ts:422
item: WorkspaceItem
itemKind: UserDraftItemKind
storagePath: string
message: string
}
// Persist a built draft value. `UserDraft.seed` reflects it into an open editor's
// cell WITHOUT a double-POST (no-ops if no cell; its seedNextWrite suppresses the
// cell's autosave mirror), then the awaited immediate save is the single source of
// persistence + conflict detection against the shared baseline. force overwrites.
export async function persistGlobalDraft(
workspace: string,
type: WorkspaceItemType,
path: string,
value: unknown,
opts: { triggerKind?: TriggerKind; force?: boolean } = {}
): Promise<DraftPersistResult> {
const itemKind = itemKindFor(type, opts.triggerKind)
if (!itemKind) throw new Error(`Unsupported draft type "${type}".`)
const storagePath = resolveDraftStoragePath(workspace, itemKind, path)
UserDraft.seed(itemKind, storagePath, value, { workspace })
await UserDraftDbSyncer.save({
workspace,
itemKind,
path: storagePath,
value,
immediate: true,
force: opts.force
})
const { displayPath, isLiveDraft } = liveDisplayPath(workspace, itemKind, storagePath)
const item = userDraftEntryToWorkspaceItem(
{ workspace, itemKind, path: storagePath, value },
displayPath,
isLiveDraft
)
if (!item) throw new Error(`Could not synthesize ${type} draft "${path}".`)
// A failed save (network/5xx) is recorded in the syncer's failure map, notView on GitHub (pinned to e474e8803c)
Solutions
- Set opts.triggerKind when type is 'trigger' (e.g. { triggerKind: 'websocket' }).
- Use only supported WorkspaceItemType values: script, flow, resource, variable, app, schedule, trigger.
- If you added a new item type, extend the switch in itemKindFor (userDraftAdapter.ts:84) to map it to a UserDraftItemKind.
- Check the value at runtime: if itemKindFor(type, triggerKind) is undefined, surface a friendly message instead of calling persistGlobalDraft.
Example fix
// before
await persistGlobalDraft(workspace, 'trigger', path, value)
// after
await persistGlobalDraft(workspace, 'trigger', path, value, { triggerKind: 'websocket' }) Defensive patterns
Strategy: validation
Validate before calling
import { itemKindFor } from '$lib/components/copilot/chat/global/userDraftAdapter'
if (!itemKindFor(type, opts?.triggerKind)) {
throw new Error(`Cannot draft item of type "${type}"` + (type === 'trigger' && !opts?.triggerKind ? ' without a triggerKind' : ''))
} Type guard
function isDraftableType(type: WorkspaceItemType, triggerKind?: TriggerKind): boolean {
return itemKindFor(type, triggerKind) !== undefined
} Try / catch
try {
await persistGlobalDraft(workspace, type, path, value, opts)
} catch (e) {
if (e instanceof Error && e.message.startsWith('Unsupported draft type')) {
notify(`"${type}" drafts are not supported`)
} else throw e
} Prevention
- Always pass triggerKind when drafting triggers
- Keep itemKindFor's switch in sync whenever WorkspaceItemType gains a member (it is exhaustive — rely on TS)
- Validate tool arguments against the supported type union before invoking draft APIs
When it happens
Trigger: Calling persistGlobalDraft/saveGlobalAppDraft with a WorkspaceItemType outside {'script','flow','resource','variable','app','schedule','trigger'}; or type==='trigger' without opts.triggerKind set (itemKindFor returns undefined for kindless triggers).
Common situations: A chat tool passes a new workspace item type added to the app but not yet mapped in itemKindFor; a tool builds the call dynamically and drops the triggerKind; typos like 'raw_app' passed directly (chat type is 'app').
Related errors
- Invalid note at index ${index}: position must be an object w
- Invalid note at index ${index}: size must be an object with
- Invalid note at index ${index}: contained_node_ids must be a
- path or hash_ must be provided
- VALIDATION_ERROR
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/385b8604164df880.
Report an issue: GitHub.