windmill-labs/windmill · error
res.error ?? 'discard failed'
Error message
res.error ?? 'discard failed'
What it means
discardItemDraft calls discardDraft and throws when the API reports success=false, surfacing the backend error message (or a generic 'discard failed'). It is the per-item worker for the bulk-discard action.
Source
Thrown at frontend/src/lib/components/home/bulkActions.ts:124
})
}
async function deleteItem(ctx: BulkContext, item: BulkItem): Promise<void> {
if (item.kind === 'script') {
await ScriptService.deleteScriptByPath({ workspace: ctx.workspace, path: item.path })
} else if (item.kind === 'flow') {
await FlowService.deleteFlowByPath({ workspace: ctx.workspace, path: item.path })
} else {
await AppService.deleteApp({ workspace: ctx.workspace, path: item.path })
}
}
async function discardItemDraft(ctx: BulkContext, item: BulkItem): Promise<void> {
// The draft overlay is the one place a raw app is its own kind.
const kind = item.kind === 'app' && item.rawApp ? 'raw_app' : item.kind
// invalidate=false: the caller refreshes the draft list once for the batch.
const res = await discardDraft(kind, item.path, ctx.workspace, item.draftOnly, false, false)
if (!res.success) throw new Error(res.error ?? 'discard failed')
}
export type BulkOutcome = { item: BulkItem; error?: string }
/**
* Apply `action` to each item in turn, never aborting the batch on a failure —
* every item gets its own outcome so partial success is reported rather than
* hidden. `onProgress` is called after each item with the number completed.
*/
export async function runBulk(
action: BulkAction,
items: BulkItem[],
ctx: BulkContext,
opts: { target?: string; onProgress?: (done: number) => void } = {}
): Promise<BulkOutcome[]> {
const outcomes: BulkOutcome[] = []
for (const item of items) {
try {View on GitHub (pinned to e474e8803c)
Solutions
- Refresh the drafts list and retry the bulk discard on the remaining items.
- Check res.error in the bulk outcome for the specific backend message (404 vs 403) and act on it.
- Verify workspace and permissions for the failing item's path.
- Re-run the bulk action — runBulk continues per item, so only failed items remain.
Example fix
// before
await runBulk('discard', items)
// after
const outcomes = await runBulk('discard', items)
const stale = outcomes.filter(o => o.error).map(o => o.item)
await refreshDrafts(); await runBulk('discard', stale) Defensive patterns
Strategy: try-catch
Validate before calling
const fresh = await listDrafts(workspace) const discardable = items.filter(i => fresh.some(d => d.path === i.path))
Try / catch
const outcomes = await runBulk(ctx, 'discard', items, opts)
for (const o of outcomes.filter(o => o.error)) console.warn('discard failed', o.item.path, o.error) Prevention
- Refresh the draft list before bulk discards in shared workspaces
- Check per-item BulkOutcome errors instead of assuming all succeeded
- Verify permissions on the target workspace before the batch
When it happens
Trigger: Bulk-discard run against an item whose draft no longer exists (already discarded), a path the user no longer has permission for, or a workspace/API error returned in res.error.
Common situations: Two people discarding the same drafts concurrently; stale draft list opened while drafts were deleted elsewhere; permission loss after the list was loaded.
Related errors
- Failed to create flow ${remotePath}: ${e.body ?? e.message}
- state.failureMessage ?? `Failed to delete draft "${path}".`
- Draft "${path}" changed externally since you last read it; i
- Failed to execute test run: ${errorMessage}
- Failed to create schedule "${requestBody.path}": ${formatToo
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/b7c26e84e109bd02.
Report an issue: GitHub.