microsoft/TypeScript · error · Error

Cannot shadow a mutable file system.

Error message

Cannot shadow a mutable file system.

What it means

Thrown by FileSystem.shadow (vfsUtil.ts:162) when asked to shadow a file system that is not yet read-only. Shadowing shares data lazily between the original and the copy; allowing it on a mutable FS would let the original keep mutating state the shadow has already inherited. Callers must freeze the source first via makeReadonly().

Source

Thrown at src/harness/vfsUtil.ts:162

        if (this.isReadonly) return;
        const fs = new FileSystem(this.ignoreCase, { time: this._time });
        fs._lazy = this._lazy;
        fs._cwd = this._cwd;
        fs._time = this._time;
        fs._shadowRoot = this._shadowRoot;
        fs._dirStack = this._dirStack;
        fs.makeReadonly();
        this._lazy = {};
        this._shadowRoot = fs;
    }

    /**
     * Gets a shadow copy of this file system. Changes to the shadow copy do not affect the
     * original, allowing multiple copies of the same core file system without multiple copies
     * of the same data.
     */
    public shadow(ignoreCase: boolean = this.ignoreCase): FileSystem {
        if (!this.isReadonly) throw new Error("Cannot shadow a mutable file system.");
        if (ignoreCase && !this.ignoreCase) throw new Error("Cannot create a case-insensitive file system from a case-sensitive one.");
        const fs = new FileSystem(ignoreCase, { time: this._time });
        fs._shadowRoot = this;
        fs._cwd = this._cwd;
        return fs;
    }

    /**
     * Gets or sets the timestamp (in milliseconds) used for file status, returning the previous timestamp.
     *
     * @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/time.html
     */
    public time(value?: number): number {
        if (value !== undefined) {
            if (this.isReadonly) throw createIOError("EPERM");
            this._time = value;
        }
        else if (!this.isReadonly) {

View on GitHub (pinned to b465fdbfe1)

Solutions

  1. Call `fs.makeReadonly()` immediately before `fs.shadow()`.
  2. Prefer `fs.snapshot()` if you actually want to fork the current mutable state — snapshot internally freezes a clone and re-points _shadowRoot.
  3. Freeze the base VFS once in beforeAll/module init, then shadow per test.
  4. Audit setup helpers to guarantee makeReadonly is called before any shadow.

Example fix

// before
const base = new VirtualFileSystem(/* ... */);
populate(base);
const fork = base.shadow(); // throws

// after — freeze first
const base = new VirtualFileSystem(/* ... */);
populate(base);
base.makeReadonly();
const fork = base.shadow();
Defensive patterns

Strategy: validation

Validate before calling

// Freeze before shadowing.
if (!fs.isReadonly) fs.makeReadonly();
const fork = fs.shadow();

Type guard

function isReadOnlyFs(fs: FileSystem): boolean {
  return fs.isReadonly === true;
}

Prevention

When it happens

Trigger: Test code calls `fs.shadow()` on a freshly-built virtual FS that has not yet been frozen. The constructor leaves isReadonly false; shadowing requires the source to be immutable so the copy-on-write invariants hold.

Common situations: Building a shared base VFS once and forking shadows per test without freezing; refactoring setup code that previously called makeReadonly; copy-pasting shadow usage from a readonly fixture into a mutable one.

Related errors


AI-assisted analysis of microsoft/TypeScript@b465fdbfe1 (2026-08-12). Data as JSON: /api/errors/cf53c13cf2963fba. Report an issue: GitHub.