{"record":{"id":"469787734f8bd489","repo":"wavetermdev/waveterm","slug":"missing-zone-file-info-for-zoneid-filename","errorCode":null,"errorMessage":"missing zone file info for ${zoneId}:${fileName}","messagePattern":"missing zone file info for (.+?):(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"frontend/app/store/global.ts","lineNumber":471,"sourceCode":"    const usp = new URLSearchParams();\n    usp.set(\"zoneid\", zoneId);\n    usp.set(\"name\", fileName);\n    if (offset != null) {\n        usp.set(\"offset\", offset.toString());\n    }\n    const resp = await fetch(getWebServerEndpoint() + \"/wave/file?\" + usp.toString());\n    if (!resp.ok) {\n        if (resp.status === 404) {\n            return { data: null, fileInfo: null };\n        }\n        throw new Error(\"error getting wave file: \" + resp.statusText);\n    }\n    if (resp.status == 204) {\n        return { data: null, fileInfo: null };\n    }\n    const fileInfo64 = resp.headers.get(\"X-ZoneFileInfo\");\n    if (fileInfo64 == null) {\n        throw new Error(`missing zone file info for ${zoneId}:${fileName}`);\n    }\n    const fileInfo = JSON.parse(base64ToString(fileInfo64));\n    const data = await resp.arrayBuffer();\n    return { data: new Uint8Array(data), fileInfo };\n}\n\nfunction setNodeFocus(nodeId: string) {\n    const layoutModel = getLayoutModelForStaticTab();\n    layoutModel.focusNode(nodeId);\n}\n\nconst objectIdWeakMap = new WeakMap();\nlet objectIdCounter = 0;\nfunction getObjectId(obj: any): number {\n    if (!objectIdWeakMap.has(obj)) {\n        objectIdWeakMap.set(obj, objectIdCounter++);\n    }\n    return objectIdWeakMap.get(obj);","sourceCodeStart":453,"sourceCodeEnd":489,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/frontend/app/store/global.ts#L453-L489","documentation":"fetchWaveFile reads a wave file's bytes and metadata from the backend over HTTP; the server is expected to return the file's WaveFileInfo as a base64-encoded JSON blob in the X-ZoneFileInfo response header. This error is thrown when a successful (non-204) response arrives without that header, meaning the backend did not supply the metadata the client requires to construct the file entry. It indicates a server-side response inconsistency or a stale/unresolvable zone-file reference.","triggerScenarios":"Calling fetchWaveFile(zoneId, fileName) where the backend returns 200 but omits the X-ZoneFileInfo header — e.g. the zone/file exists in the routed route context but the zone file metadata is missing on disk, a proxy stripped the custom header, or hitting a stale web server build that predates the header.","commonSituations":"Reverse proxy or CSP middleware filtering non-standard X-* headers; mixed-version deployment where an old backend serves a new frontend; file was deleted from the zone dir between listing and fetch; pointing the frontend at the wrong endpoint via VITE/Wave server config.","solutions":["Check the raw response in devtools/network tab to confirm X-ZoneFileInfo is actually absent vs. unreadable","Verify the wave backend serving the request is up-to-date and running the same version as the frontend","Ensure no proxy/middleware strips X-ZoneFileInfo; add it to any header allowlist","Re-check zoneId/fileName spelling; force a refresh of the file listing cache","If the file is gone, handle it: re-fetch the zone file listing and remove stale references"],"exampleFix":"// before\nconst fileInfo64 = resp.headers.get(\"X-ZoneFileInfo\");\nif (fileInfo64 == null) {\n    throw new Error(`missing zone file info for ${zoneId}:${fileName}`);\n}\n// after\nconst fileInfo64 = resp.headers.get(\"X-ZoneFileInfo\");\nif (fileInfo64 == null) {\n    console.warn(`missing zone file info for ${zoneId}:${fileName}, refreshing zone listing`);\n    await refreshZoneFileListing(zoneId);\n    return { data: null, fileInfo: null };\n}","handlingStrategy":"fallback","validationCode":"const resp = await fetch(url);\nconst hasInfo = resp.ok && resp.status !== 204 && resp.headers.has(\"X-ZoneFileInfo\");\nif (!hasInfo) {\n    await refreshZoneFileListing(zoneId);\n    return null;\n}","typeGuard":"function hasZoneFileInfo(resp: Response): boolean {\n    return resp.ok && resp.status !== 204 && resp.headers.get(\"X-ZoneFileInfo\") != null;\n}","tryCatchPattern":"try {\n    const { data, fileInfo } = await fetchWaveFile(zoneId, fileName);\n    // use data/fileInfo\n} catch (e) {\n    if (String(e).includes(\"missing zone file info\")) {\n        // treat file as unavailable: purge cache entry, refresh listing, or show placeholder\n    } else {\n        throw e;\n    }\n}","preventionTips":["Never strip X-* headers in proxies/middleware","Keep frontend and backend versions in lockstep","Treat 204 as the only 'valid empty' response and purge stale cache entries on this error","Log zoneId:fileName with the error for quick triage"],"tags":["http","headers","file-io","wave"],"backgroundTag":"missing-response-header","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}