Skip to content

[Bug]: native Windows: runtime.checkpoint_artifacts capture and materialize always fail because the copied-file durability flush uses a read-only handle #6562

Closed

Description

@JasonBuildAI

Preflight

  • I searched existing issues and discussions for this behavior.
  • This report is not an unpatched security vulnerability.
  • The report and attachments contain no credentials or private/internal material.

Issue origin

Observed or reproduced in a real environment

LoopX version or commit

main at 81ff72b64 (release 1.4.2), source checkout on native Windows 11 with Node 22 (node --experimental-strip-types). The failing assertion is host-independent of the checkout: any native Windows run of the checkpoint artifact suite reproduces it.

Host or runtime surface

Not host-specific: the shipped control-plane effect runtime.checkpoint_artifacts, reached by loopx research-checkpoint capture, the Codex and research checkpoint hosts, and every materialize caller. The defect is Windows-only.

LoopX area

Control plane (goals, todos, quota, scheduler, registry, runtime)

Problem

On a native Windows host, runtime.checkpoint_artifacts can never capture or materialize a checkpoint unit. copyComponent in loopx/control_plane/runtime/checkpoint_artifacts.ts flushes every freshly copied member through a read-only handle (open(destination, "r") + handle.sync()), and Windows FlushFileBuffers (Node fsync) requires update access to the handle, so each capture aborts with EPERM -4048 fsync and no unit is published. Users on Windows cannot capture a checkpoint at all, and materialize cannot stage the unit it restores.

The read-only reopen is deliberate and recent: #6415 changed this call site from open(destination, "r+") to open(destination, "r") because copyFile preserves a read-only participant's mode (e.g. 0o444) and r+ then fails with EACCES for an ordinary non-root POSIX controller. The durability flush itself is also deliberate: #6258 fixed the identical read-only-handle flush in the backup-state owner (loopx/control_plane/coordination/sqlite_backup.ts), where the same Windows EPERM -4048 had blocked state backups. A correct fix must keep the flush on every platform and satisfy both cases: Windows needs an update handle for FlushFileBuffers, while a POSIX read-only copy denies r+ but can sync through a read-only handle.

Minimal reproduction

On native Windows at 81ff72b64 with Node 22:

node --no-warnings --experimental-sqlite --experimental-strip-types --test tests/control_plane_ts/checkpoint_artifacts.test.ts

8 of 13 tests fail with the error below; the capture/materialize cases and the SQLite snapshot cases all abort in copyComponent. The same failure reproduces through the shipped runtime entry, e.g. effect_runtime_result("runtime.checkpoint_artifacts", {"action": "capture", ...}) with any component.

Expected behavior

Capture publishes the unit, materialize restores it byte-for-byte with the reviewed modes, and the durability flush is retained, on Windows exactly as on POSIX.

Actual behavior

Every capture/materialize aborts with Error: EPERM: operation not permitted, fsync (errno -4048) from copyComponent, so no checkpoint unit can be created or restored on Windows.

Sanitized diagnostics

test at tests\control_plane_ts\checkpoint_artifacts.test.ts:24:1
  Error: EPERM: operation not permitted, fsync
      at async fsync (node:internal/fs/promises:1654:10)
      at async copyComponent (loopx/control_plane/runtime/checkpoint_artifacts.ts:141:10)
      at async copyComponent (loopx/control_plane/runtime/checkpoint_artifacts.ts:132:7)
      at async checkpointArtifacts (loopx/control_plane/runtime/checkpoint_artifacts.ts:320:30)
    errno: -4048, code: 'EPERM', syscall: 'fsync'

Probe results on the same host: open(file, "r") + sync() fails EPERM -4048 even for a writable file; open(file, "r+") + sync() succeeds; for a read-only copy, r+ fails EPERM, and after granting owner write access the update handle sync succeeds with the mode restored afterwards.

Additional context

This is the same defect class as #6258 (merged) and was introduced by #6415 (merged). A PR that keeps the flush, opens the copied member for update, and temporarily grants owner access only to the staged copy when the copy itself is read-only (restoring the reviewed mode before publication) is being prepared.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions