[BUG] Desktop app refuses ALL session transcripts (ETRANSCRIPTNOTREGULAR) on FSLogix/RDS profiles — lstat/fstat volume-serial mismatch false-positives the transcript identity check

Status Open
Reported on v2.1.247
Maintainer reply None cached
Activity 0 comments · opened Aug 31, 2026

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 nonRegularETRANSCRIPTNOTREGULAR → the UI renders empty history.

Relocating the config root off the profile volume (junction ~/.claudeC:\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 hash f65e386db0db64c8f8b39950e25adb11f5f5e3f3 — same behavior on the Store-installed package and on a fresh sideload via Claude-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%\FSLogix present; 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>\.claudeC:\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.

  1. 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.
  2. Fully quit the Claude desktop app and reopen it.
  3. Open history: every session renders blank. %LOCALAPPDATA%\Claude\logs\main.log shows, 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)
``

  1. Confirm the files themselves are healthy: fsutil reparsepoint query <file> → "not a reparse point"; attributes are plain Archive; nlink=1.
  2. 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);
})();
`
Run:
node repro.js "C:\Users\<user>\.claude\projects\<project>\<session>.jsonl"
Observed on the affected machine: lstat dev=
2922441824 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 nonRegularETRANSCRIPTNOTREGULAR`.

  1. Control test: copy the same .jsonl outside the profile (e.g. C:\Temp) and rerun repro.js — dev/ino now match. Relocate the whole ~/.claude off the profile volume (junction at the home level) and relaunch the app: history loads and the log shows scanned 5/5 transcript files with 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_

View original on GitHub ↗