windmill-labs/windmill · warning
failed to read ${kind} ${path} in ${workspace}
Error message
failed to read ${kind} ${path} in ${workspace} What it means
Thrown by diffSnapshot.ts when reading the server-side value of an item to build a diff snapshot and the read comes back empty (null or an empty object). getItemValue resolves to {} for ANY failed fetch, including 'item may not exist', but for a fork side that the comparison says exists, an empty read must be a transient failure — so the code throws instead of fabricating a misleading one-sided or matching diff. The throw is surfaced as a fetch-error entry in the diff result.
Source
Thrown at frontend/src/lib/components/copilot/chat/global/diffSnapshot.ts:975
if (kind === 'resource_type') {
const rt = await ResourceService.getResourceType({ workspace, path })
return {
value: {
schema: rt.schema,
description: rt.description,
format_extension: rt.format_extension,
is_fileset: rt.is_fileset
},
valueMasked: false
}
}
const value = await getItemValue(kind as DeployKind, path, workspace)
// getItemValue reads `{}` for ANY failed fetch ("item may not exist") — but
// a fork side is only fetched when the comparison lists it as existing, so
// an empty read is a transient failure. Erroring (surfaced as a fetch-error
// entry) beats fabricating a one-sided or matching diff out of it.
if (value == null || (typeof value === 'object' && Object.keys(value).length === 0)) {
throw new Error(`failed to read ${kind} ${path} in ${workspace}`)
}
if ((kind === 'app' || kind === 'raw_app') && value !== null && typeof value === 'object') {
const row = value as Record<string, unknown>
// Raw apps: project onto the flat files/runnables draft shape so per-file
// splitting works and the sides match the draft-mode canonicalization.
// parent_version is a per-workspace version counter — never comparable
// across workspaces. Inline-script locks are server-recomputed noise.
if (kind === 'raw_app' || row.raw_app === true) {
const canonical = appSourceToDraftValue(row) as Record<string, unknown>
delete canonical.parent_version
const runnables = canonical.runnables as Record<string, any> | undefined
if (runnables) {
for (const k of Object.keys(runnables)) {
if (runnables[k]?.inlineScript?.lock != undefined) {
runnables[k].inlineScript.lock = undefined
}
}
}View on GitHub (pinned to e474e8803c)
Solutions
- Retry the diff — the error is documented as a transient-failure guard, so a re-run after the hiccup usually succeeds.
- Verify the item still exists in the source workspace and the fork is up to date (re-sync the fork).
- Check permissions/network for the workspace being read, then retry.
Example fix
// before (transient empty read kills the diff)
await diffSnapshot(...) // Error: failed to read script f/etl in fork-ws
// after: retry with backoff
try { await diffSnapshot(...) } catch { await sleep(1000); await diffSnapshot(...) } Defensive patterns
Strategy: retry
Validate before calling
const exists = await itemExistsInListing(kind, path, workspace)
if (!exists) {
throw new Error(`${kind} ${path} not listed in ${workspace}; skip diff`)
} Type guard
function isEmptyRead(v) {
return v == null || (typeof v === 'object' && Object.keys(v).length === 0)
} Try / catch
try {
await diffSnapshot(...)
} catch (e) {
if (/^failed to read /.test(e.message)) {
await sleep(1000)
return diffSnapshot(...) // transient; retry once
}
throw e
} Prevention
- Retry diffs shortly after failures — empty reads are treated as transient.
- Re-sync the fork before diffing right after fork creation.
- Confirm the item still exists and permissions are valid for both workspaces.
When it happens
Trigger: Building a workspace/fork diff when getItemValue(kind, path, workspace) returns null or an empty object for a side that the comparison listing says exists — network hiccup, permission error, or a race where the item was deleted between listing and reading.
Common situations: Diffing against a fork right after it was created (eventual consistency); the upstream item was deleted server-side between the list call and the read; a token/permission issue scoped to the fork workspace; temporary backend 5xx.
Related errors
- ApiError with mapped HTTP status message (e.g. "Not Found",
- Generic Error: status: ${errorStatus}; status text: ${errorS
- Dependency generation failed: ${queueResponse.status} ${queu
- Failed to poll dependencies job ${jobId}: ${e?.message ?? e}
- No response body for SSE stream
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/b03742c83f848b87.
Report an issue: GitHub.