windmill-labs/windmill · error · Error
Draft diff not supported for kind ${kind}
Error message
Draft diff not supported for kind ${kind} What it means
getDraftDiffValues in frontend/src/lib/utils_draft_deploy.ts (line 345) computes a deployed-vs-draft diff by looking up a getter in the OVERLAY_GETTERS map keyed by item kind, then canonicalizing both sides onto a shared field set. When the kind has no entry in OVERLAY_GETTERS (the map has no draft overlay representation for it), the code throws 'Draft diff not supported for kind ...'. Unlike the trigger dispatchers, this one intentionally guards against kinds whose draft shapes are too divergent to diff meaningfully.
Source
Thrown at frontend/src/lib/utils_draft_deploy.ts:345
return {
deployed: draftOnly ? EMPTY_DEPLOYED.app!(undefined) : deployed,
draft: {
summary: draftParts.summary ?? r.summary ?? '',
value: draftParts.value,
path: draftParts.draftPath ?? r.path
},
hasDraft: r.draft != null,
noDeployed: r.no_deployed === true
}
} else {
// Variables / resources / schedules / triggers: one overlay GET yields
// both sides, but the draft side is the editor's state shape while the
// deployed side is the backend row — they diverge enough to make a raw
// diff pure noise. Canonicalize both onto a shared field set (same shaping
// the compare page's `getItemValue` applies) so only real changes show.
const getter = OVERLAY_GETTERS[kind]
if (!getter) {
throw new Error(`Draft diff not supported for kind ${kind}`)
}
const { deployed, draft, hasDraft, noDeployed } = splitOverlay(await getter(workspace, path))
return {
deployed: draftOnly ? {} : canonicalizeDraftDiffValue(kind, deployed, false),
draft: canonicalizeDraftDiffValue(kind, draft, true),
hasDraft,
noDeployed
}
}
}
/**
* Whether a draft's base is stale: the deployed version the draft forked from
* no longer matches the current deployed head — a newer version was deployed
* after the draft began, so deploying the draft would silently revert it.
* Scripts compare the draft's `parent_hash` vs the deployed `hash`; flows the
* pinned `version_id` vs the deployed head `version_id`; apps (incl. raw) the
* pinned `parent_version` vs the head of `versions`. `r` is the item fetchedView on GitHub (pinned to e474e8803c)
Solutions
- Check the `kind` in the error and confirm it should be diffable; if it is (e.g. a recently added kind), add an entry to OVERLAY_GETTERS returning { deployed, draft } for that kind.
- If the kind is not diffable by design, hide the diff action in the UI (guard with `kind in OVERLAY_GETTERS`) instead of invoking getDraftDiffValues.
- Update canonicalizeDraftDiffValue with a shaping branch for the new kind so the diff output is not raw noise.
- Ensure kind keys are in sync between the Kind union and OVERLAY_GETTERS after any rename.
Example fix
// before
const diff = await getDraftDiffValues('raw_app', workspace, path)
// after
if (kind in OVERLAY_GETTERS) {
const diff = await getDraftDiffValues(kind, workspace, path)
} else {
// hide the diff entry for this kind in the UI
} Defensive patterns
Strategy: type-guard
Validate before calling
import { OVERLAY_GETTERS } from '$lib/utils_draft_deploy'
function canDiffDraft(kind: Kind): kind is keyof typeof OVERLAY_GETTERS {
return kind in OVERLAY_GETTERS
} Type guard
const OVERLAY_GETTERS = { script: getScriptOverlay, flow: getFlowOverlay /* ... */ } as const
type DiffableKind = keyof typeof OVERLAY_GETTERS
function isDiffableKind(k: Kind): k is DiffableKind {
return k in OVERLAY_GETTERS
} Try / catch
try {
const diff = await getDraftDiffValues(kind, workspace, path)
} catch (e) {
if (String(e?.message).startsWith('Draft diff not supported for kind')) {
// fall back to the plain deploy view without a diff drawer
} else throw e
} Prevention
- Gate the diff UI action on `kind in OVERLAY_GETTERS` so unsupported kinds never reach the throw.
- When adding a new Kind member, add its OVERLAY_GETTERS entry and canonicalizeDraftDiffValue branch in the same change.
- Derive the diffable-kind list from OVERLAY_GETTERS' keys (a satisfies/typeof check) so renames fail at compile time instead of runtime.
When it happens
Trigger: Calling getDraftDiffValues (via values, diffWorkspaceItem, or the {deployed,draft} overloads) with a kind that has no OVERLAY_GETTERS entry — e.g. raw_app, folder, resource_type, datatable_migration, or a new item kind added to the Kind union without a draft-diff getter.
Common situations: Trying to show a diff drawer for a newly supported deployable kind before adding its overlay getter; a kind renamed in the Kind union while the OVERLAY_GETTERS map still uses the old key; users hitting it on draft bundles like data_pipeline that are not diffable.
Related errors
- Unexpected trigger kind ${triggerKind}. Allowed kinds are: r
- Cannot reject module without a beforeFlow snapshot
- Cannot diff drafts of kind ${itemKind}
- Unexpected trigger kind got: ${kind}
- No model selected
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/1f6b5235dde0a173.
Report an issue: GitHub.