{"record":{"id":"b03742c83f848b87","repo":"windmill-labs/windmill","slug":"failed-to-read-kind-path-in-workspace","errorCode":null,"errorMessage":"failed to read ${kind} ${path} in ${workspace}","messagePattern":"failed to read (.+?) (.+?) in (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"frontend/src/lib/components/copilot/chat/global/diffSnapshot.ts","lineNumber":975,"sourceCode":"\tif (kind === 'resource_type') {\n\t\tconst rt = await ResourceService.getResourceType({ workspace, path })\n\t\treturn {\n\t\t\tvalue: {\n\t\t\t\tschema: rt.schema,\n\t\t\t\tdescription: rt.description,\n\t\t\t\tformat_extension: rt.format_extension,\n\t\t\t\tis_fileset: rt.is_fileset\n\t\t\t},\n\t\t\tvalueMasked: false\n\t\t}\n\t}\n\tconst value = await getItemValue(kind as DeployKind, path, workspace)\n\t// getItemValue reads `{}` for ANY failed fetch (\"item may not exist\") — but\n\t// a fork side is only fetched when the comparison lists it as existing, so\n\t// an empty read is a transient failure. Erroring (surfaced as a fetch-error\n\t// entry) beats fabricating a one-sided or matching diff out of it.\n\tif (value == null || (typeof value === 'object' && Object.keys(value).length === 0)) {\n\t\tthrow new Error(`failed to read ${kind} ${path} in ${workspace}`)\n\t}\n\tif ((kind === 'app' || kind === 'raw_app') && value !== null && typeof value === 'object') {\n\t\tconst row = value as Record<string, unknown>\n\t\t// Raw apps: project onto the flat files/runnables draft shape so per-file\n\t\t// splitting works and the sides match the draft-mode canonicalization.\n\t\t// parent_version is a per-workspace version counter — never comparable\n\t\t// across workspaces. Inline-script locks are server-recomputed noise.\n\t\tif (kind === 'raw_app' || row.raw_app === true) {\n\t\t\tconst canonical = appSourceToDraftValue(row) as Record<string, unknown>\n\t\t\tdelete canonical.parent_version\n\t\t\tconst runnables = canonical.runnables as Record<string, any> | undefined\n\t\t\tif (runnables) {\n\t\t\t\tfor (const k of Object.keys(runnables)) {\n\t\t\t\t\tif (runnables[k]?.inlineScript?.lock != undefined) {\n\t\t\t\t\t\trunnables[k].inlineScript.lock = undefined\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}","sourceCodeStart":957,"sourceCodeEnd":993,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/frontend/src/lib/components/copilot/chat/global/diffSnapshot.ts#L957-L993","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before (transient empty read kills the diff)\nawait diffSnapshot(...) // Error: failed to read script f/etl in fork-ws\n\n// after: retry with backoff\ntry { await diffSnapshot(...) } catch { await sleep(1000); await diffSnapshot(...) }","handlingStrategy":"retry","validationCode":"const exists = await itemExistsInListing(kind, path, workspace)\nif (!exists) {\n  throw new Error(`${kind} ${path} not listed in ${workspace}; skip diff`)\n}","typeGuard":"function isEmptyRead(v) {\n  return v == null || (typeof v === 'object' && Object.keys(v).length === 0)\n}","tryCatchPattern":"try {\n  await diffSnapshot(...)\n} catch (e) {\n  if (/^failed to read /.test(e.message)) {\n    await sleep(1000)\n    return diffSnapshot(...) // transient; retry once\n  }\n  throw e\n}","preventionTips":["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."],"tags":["diff","fork","network","transient"],"backgroundTag":"empty-read-transient-failure","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}