Skip to content

Release Windows check fails: FilesystemWorkerClient rejects writes to existing files without expectedIdentity (regression from #3001) #3484

Description

@Astro-Han

FilesystemWorkerClient.execute() now rejects a plain write to an existing file when the caller doesn't supply expectedIdentity. Callers that go through FilesystemExecutor are fine — it captures the identity at lock acquisition and passes it down. Callers that construct the client directly are not, and scripts/verify-windows-sandbox-e2e.mjs is one of them, so the Release Windows check is currently failing on every PR.

Introduced by #3001 (ec1579bc).

Reproduction

scripts/verify-windows-sandbox-e2e.mjs:95-97:

const insidePath = join(workspace, 'inside.txt');
await writeFile(insidePath, 'seeded');
await execute({ kind: 'write', path: insidePath, content: 'packaged-relay-ok' });  // throws
FilesystemWorkerClientError: The target was created while this call waited for the lock; re-read before writing.
  code: 'SANDBOX_FILESYSTEM_OPERATION_FAILED'
  domain: 'filesystem'
  reason: 'path_changed'
  stage: 'validation'
  at verifyWindowsSandboxWorkerE2E (scripts/verify-windows-sandbox-e2e.mjs:97:5)

There is no race here. The file is created by the line above, single-threaded and awaited. execute is client.execute({ operation, cwd, mode }) — no expectedIdentity.

Cause

packages/runtime/src/filesystem-worker/client.ts:209-219:

const identity =
  input.expectedIdentity && target.targetType !== 'missing'
    ? input.expectedIdentity
    : undefined;
if (!identity && target.targetType !== 'missing' && access === 'write') {
  throw clientError('path_changed', 'validation', requestId, '...');
}

expectedIdentity is optional (client.ts:67) with no internal fallback — it appears only at lines 67, 194, 210 and 211. So the condition reduces to: any write to an existing target from a caller that didn't supply a T0 identity fails.

The reconciliation logic itself is right, and the comment at client.ts:194 correctly explains why the identity must not be re-derived after the lock is held. What's missing is the third case: a caller that has no T0 identity at all isn't a victim of a swap — it just isn't participating in the CAS. Today that's indistinguishable from "T0 missing, T1 existing".

Scope

  • Not user-facing. Tool-driven writes reach the worker through FilesystemExecutor, which captures identity via captureIdentityAtLockAcquisition before the lock wait (filesystem-executor.ts:270 for write/edit/format_json, :284 for apply_patch) and forwards it.
  • Direct FilesystemWorkerClient consumers are affected. The known one is the Windows sandbox e2e verifier; worth grepping for others before fixing.
  • Ordinary CI is green, which is why this only surfaced on the Windows release job.

Suggested direction

Distinguish "caller supplied no identity" from "T0 saw nothing". Either make expectedIdentity required at the type level so every direct caller has to decide explicitly, or add an opt-out the verifier can pass to say it isn't participating in the CAS. The first is stricter and would have caught this at compile time.

Whoever picks this up: scripts/verify-windows-sandbox-e2e.mjs reproduces it directly, so the fix is verifiable without a full Windows packaging run.


Filed after this failure blocked #3472's Release Windows check. Reported by an AI-assisted review process; the reproduction and cause were verified against the source at ec1579bc.

Activity

  1. Astro-Han commented on Aug 22, 2026

    @Astro-Han
    ContributorAuthor

    Scope update: this blocks the Release Windows check on every open PR, not just one script.

    Confirmed failing with the identical error on at least #3454, #3434, #3431 and #3397. Any PR that runs the Windows release job hits it, because they all reach scripts/verify-windows-sandbox-e2e.mjs. So the Windows release verification path is effectively down until this is fixed — PRs can still merge on ordinary CI, but without that check.

    The product runtime remains unaffected, as described above: tool-driven writes reach the worker through FilesystemExecutor, which captures the identity before the lock wait and forwards it. This is a broken verification channel, not a broken product.

    Raising visibility given the breadth. The fix is still small — distinguish "the caller supplied no identity" from "T0 saw nothing" — and scripts/verify-windows-sandbox-e2e.mjs reproduces it in three lines without a full Windows packaging run.

  2. 1625567290 commented on Aug 22, 2026

    @1625567290
    Contributor

    I will take this and send a focused fix.

  3. added a commit that references this issue on Aug 22, 2026
    e6a58db
  4. added a commit that references this issue on Aug 22, 2026
    fe71aa5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinghelp wantedExtra attention is needed

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions