windmill-labs/windmill · error
Could not synthesize ${type} draft "${path}".
Error message
Could not synthesize ${type} draft "${path}". What it means
After storing the draft value, persistGlobalDraft converts the stored UserDraftEntry back into a chat WorkspaceItem via userDraftEntryToWorkspaceItem(). That converter dispatches on itemKind and delegates to per-kind converters (scriptDraftToWorkspaceItem, flowDraftToWorkspaceItem, ...) which return undefined when the value does not match the expected shape for that kind. If no WorkspaceItem can be synthesized, this error is thrown — the in-memory seed is then already dirty, so callers should treat it as an internal consistency failure.
Source
Thrown at frontend/src/lib/components/copilot/chat/global/userDraftAdapter.ts:439
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, not
// thrown — so check it before reporting success, else a write tool would tell
// the chat "saved" while the DB-backed source of truth was never updated.
const saveState = UserDraftDbSyncer.getState({ workspace, itemKind, path: storagePath })
if (saveState.state === 'failed') {
return {
status: 'error',
item,
itemKind,
storagePath,
message: saveState.failureMessage ?? 'Draft save failed'
}
}
const conflict = opts.force
? undefined
: UserDraftDbSyncer.getConflict({ workspace, itemKind, path: storagePath }).conflict
if (conflict) {
return {View on GitHub (pinned to e474e8803c)
Solutions
- Validate the draft value against the kind's expected shape (NewScript, Flow, AppDraftValue, ResourceDraftState, etc.) before calling persistGlobalDraft.
- Log entry.itemKind and the value when this fires to see which converter returned undefined.
- If type is a trigger, ensure its draft kind is chat-addressable (has an entry in TRIGGER_KIND_BY_DRAFT_KIND) and the value matches TriggerRequestBody.
- Extend the corresponding xxxDraftToWorkspaceItem converter if a legitimate new shape was rejected.
Example fix
// before
await persistGlobalDraft(ws, 'script', path, value)
// after
if (!value || typeof value !== 'object' || !('content' in value)) throw new Error('Invalid script draft value')
await persistGlobalDraft(ws, 'script', path, value) Defensive patterns
Strategy: validation
Validate before calling
function isPlausibleDraftValue(type: WorkspaceItemType, value: unknown): boolean {
return typeof value === 'object' && value !== null && Object.keys(value).length > 0
}
if (!isPlausibleDraftValue(type, value)) throw new Error(`Malformed ${type} draft value`) Type guard
function isRecord(v: unknown): v is Record<string, unknown> {
return typeof v === 'object' && v !== null && !Array.isArray(v)
} Try / catch
try {
await persistGlobalDraft(workspace, type, path, value, opts)
} catch (e) {
if (e instanceof Error && e.message.startsWith('Could not synthesize')) {
console.error('Draft value rejected by converter', { type, path, value })
notify(`The ${type} draft value is not in the expected format`)
} else throw e
} Prevention
- Validate model-produced draft values against the kind's schema (NewScript, Flow, etc.) before persisting
- Log the offending value when converters return undefined to catch shape drift early
- Keep xxxDraftToWorkspaceItem converters and their input types co-located so changes stay in sync
When it happens
Trigger: persistGlobalDraft called with a value whose shape fails the per-kind converter (e.g. a script draft value missing the fields scriptDraftToWorkspaceItem requires, a malformed app/flow value); or an itemKind that has no converter (chat-unaddressable kinds like webhook/poll/cli trigger drafts reaching the write path).
Common situations: An AI chat write tool receives a loosely-typed JSON 'value' argument from the model and forwards it unvalidated; version drift where a converter was tightened but callers still send the old shape.
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
- Unsupported draft type "${type}".
- Invalid group
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/d5e65189a679b207.
Report an issue: GitHub.