[BUG] Desktop app refuses ALL session transcripts (ETRANSCRIPTNOTREGULAR) on FSLogix/RDS profiles — lstat/fstat volume-serial mismatch false-positives the transcript identity check
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
[BUG] Desktop app refuses ALL session transcripts (ETRANSCRIPTNOTREGULAR) on FSLogix/RDS profiles — lstat/fstat volume-serial mismatch false-positives the transcript identity check
Summary
On Windows Server with FSLogix profile containers (standard RDS/AVD setup), the Claude desktop app shows blank history for every session on every launch. The transcripts on disk are perfectly healthy; the app's transcript-open identity check (lstat dev/ino vs open-handle fstat dev/ino) deterministically fails for every file inside the user profile, because by-path stat and by-handle stat report different volume serials when the profile is an FSLogix-mounted VHD. Every transcript is classified nonRegular → ETRANSCRIPTNOTREGULAR → the UI renders empty history.
Relocating the config root off the profile volume (junction ~/.claude → C:\ClaudeData\.claude, the relocation the code itself documents as supported) fully resolves it — confirming the check, not the data, is the problem.
Environment
- App: Claude desktop 1.40609.0.0 (MSIX, package family
Claude_pzs8sxrjxfjjc), build hashf65e386db0db64c8f8b39950e25adb11f5f5e3f3— same behavior on the Store-installed package and on a fresh sideload viaClaude-Setup-x64.exe(which installs the identical MSIX, so reinstalling cannot help) - OS: Windows Server 2025 Standard 10.0.26100, accessed over RDP (RDS)
- Profile: FSLogix profile container (
%LOCALAPPDATA%\FSLogixpresent; user profile mounted from VHD) - Claude Code runtime bundled with the app: 2.1.247
Symptom
On every app launch, for every session jsonl under ~/.claude/projects/**:
[warn] [CCD] Not loading transcript <id>.jsonl: the path is not a plain regular file (a symlink at the file itself, a FIFO, or an unverifiable reparse point) — it is not followed or waited on; further refusals of this file this launch are not logged
[warn] loadTranscriptTailFromDisk failed for session <id>; falling through to full drain transcript is not a regular file { code: 'ETRANSCRIPTNOTREGULAR', ... }
[info] [CCD] /stats (worker) scanned 5/5 transcript files since 2026-03-02; 5 file error(s)
History panes render blank after every close/reopen. The bundled CLI (claude --resume) reads the same files fine, and plain Node/PowerShell reads inside the app's own container also work — only the identity check refuses them.
Root cause
In app.asar (index2.chunk-*.js), the transcript open helper does, in order: lstat(path, {bigint:true}) → open(path, O_RDONLY) → handle.stat({bigint:true}), then:
if (r !== null) {
let t = r.dev===0n || r.ino===0n || n.dev===0n || n.ino===0n;
if (!t && (r.dev !== n.dev || r.ino !== n.ino)) return { kind: `nonRegular` }; // ← fires
...
}
On an FSLogix-mounted profile, by-path lstat and by-handle fstat disagree on the volume identity for every file in the profile — this is not a TOCTOU race, it is a stable property of the mount:
Measured on a real transcript (~/.claude/projects/.../a90cc458-....jsonl, idle file, values reproducible on every run):
| | dev | ino |
|---|---|---|
| lstat (by path) | 2922441824 | 1407374883599673 |
| fstat (by handle) | 2962270209 | 1652278213784889 |
Both dev and ino differ, neither is 0, so the check returns nonRegular every time. The file itself is a plain regular file: attributes Archive, fsutil reparsepoint query → "not a reparse point", isSymbolicLink()=false, isFile()=true, nlink=1.
Control: the same file copied outside the profile (C:\ClaudeData\...) yields identical dev/ino from both calls and passes.
Minimal repro of the check (run under the affected profile):
const fsp = require('fs').promises;
(async () => {
const f = process.argv[2]; // any file inside the FSLogix profile
const i = await fsp.lstat(f, {bigint:true});
const h = await fsp.open(f, 'r');
const n = await h.stat({bigint:true});
await h.close();
console.log('lstat', i.dev, i.ino);
console.log('fstat', n.dev, n.ino); // different dev (and ino) on FSLogix profiles
})();
Impact
Any user on FSLogix-backed profiles — i.e. most enterprise RDS/AVD/Citrix deployments — silently loses all visible session history in the desktop app, on every single launch. Data is intact on disk, which makes it look like a rendering bug and is very hard for end users to diagnose. Reinstalling (Store or direct download) does not help.
Workaround (verified)
With the app fully closed: copy ~/.claude to a non-profile path (e.g. C:\ClaudeData\.claude), rename the original, and create a junction C:\Users\<user>\.claude → C:\ClaudeData\.claude. This matches the relocation the bundle itself documents ("symlink the whole config root at the home level (~/.claude) or point CLAUDE_CONFIG_DIR at the relocated directory"). After relaunch: scanned 5/5 transcript files with zero file errors, history renders and persists across restarts.
Caveat: the data no longer roams with the FSLogix profile, so this is a workaround, not a fix.
Suggested fix
When by-path lstat and by-handle fstat disagree on dev, don't immediately classify as nonRegular: re-lstat the path and compare size/mtime with the handle stat (the fallback the code already uses for the ino==0 case). A stable cross-volume-serial disagreement with matching size/mtimeNs/isFile is the signature of profile virtualization (FSLogix/UPD), not of a swapped file. Alternatively, resolve identity via GetFileInformationByHandleEx(FileIdInfo) on a re-opened handle instead of a by-path stat.
What Should Happen?
Session history should load and render on every launch. The transcripts are plain regular files (Archive attribute, not symlinks/FIFOs/reparse points; fsutil confirms), so the transcript open check should accept them. On FSLogix-backed profiles (standard RDS/AVD enterprise setups), by-path lstat and by-handle fstat deterministically report different volume serials (dev) and file ids (ino) for every file in the profile — so a dev/ino mismatch alone should not hard-refuse a transcript with ETRANSCRIPTNOTREGULAR. Expected: the app falls back to the size/mtimeNs re-verification it already uses for the ino==0 case (or verifies identity by handle) and loads the file. With the same files relocated off the profile volume, the app logs "scanned 5/5 transcript files" with zero errors and history renders — that behavior should happen on the profile too.
Error Messages/Logs
Steps to Reproduce
Prerequisite: a Windows Server / VDI machine where the user profile is an FSLogix profile container (standard RDS/AVD enterprise setup). Does not reproduce on plain local profiles.
- Sign in with the FSLogix-backed profile and use the Claude desktop app (MSIX, 1.40609.0.0) for at least one Claude Code session, so transcripts exist under
C:\Users\<user>\.claude\projects\<project>\<session>.jsonl. - Fully quit the Claude desktop app and reopen it.
- Open history: every session renders blank.
%LOCALAPPDATA%\Claude\logs\main.logshows, for every transcript:
````
[warn] [CCD] Not loading transcript <id>.jsonl: the path is not a plain regular file (a symlink at the file itself, a FIFO, or an unverifiable reparse point) ...
[warn] loadTranscriptTailFromDisk failed for session <id>; ... { code: 'ETRANSCRIPTNOTREGULAR' }
[info] [CCD] /stats (worker) scanned 5/5 transcript files ...; 5 file error(s)
- Confirm the files themselves are healthy:
fsutil reparsepoint query <file>→ "not a reparse point"; attributes are plainArchive;nlink=1. - Reproduce the app's identity check with this minimal script (
repro.js):
``js`
const fsp = require('fs').promises;
(async () => {
const f = process.argv[2];
const i = await fsp.lstat(f, {bigint:true});
const h = await fsp.open(f, 'r');
const n = await h.stat({bigint:true});
await h.close();
console.log('lstat dev/ino:', i.dev, i.ino);
console.log('fstat dev/ino:', n.dev, n.ino);
})();
node repro.js "C:\Users\<user>\.claude\projects\<project>\<session>.jsonl"
Run: 2922441824
Observed on the affected machine: lstat dev= vs fstat dev=2962270209 (ino differs too) — a **deterministic** mismatch for every file inside the FSLogix profile. This is exactly the condition the transcript open helper classifies as nonRegular → ETRANSCRIPTNOTREGULAR`.
- Control test: copy the same
.jsonloutside the profile (e.g.C:\Temp) and rerunrepro.js— dev/ino now match. Relocate the whole~/.claudeoff the profile volume (junction at the home level) and relaunch the app: history loads and the log showsscanned 5/5 transcript fileswith zero file errors.
Claude Model
None
Is this a regression?
Yes, this worked in a previous version
Last Working Version
_No response_
Claude Code Version
2.1.251 (Claude Code)
Platform
Anthropic API
Operating System
Windows
Terminal/Shell
Terminal.app (macOS)
Additional Information
_No response_