[GH-ISSUE #4613] [Bug] Evidence export to FAT32 or exFAT fails and leaves staging files #2397

Closed
opened 2026-10-17 21:04:08 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @coygeek on GitHub (Oct 16, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/4613

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Evidence, artifacts, or reconstruction

Observed behavior

On macOS, evidence-export SOURCE OUTPUT --json fails on mounted FAT32 and exFAT images with exit 1 and code: cleanup_incomplete. Each default export leaves the requested destination absent and adds a .rea-write-* directory containing content and ._content. This reproduced 3/3 times per filesystem on each tested build. One additional --overwrite attempt per filesystem and build failed with the same error. The same source exports successfully on APFS.

Reproduction

Use the scripts/rea.mjs entry point from either tested build, assigned to REA_CLI, and a supported Node executable assigned to NODE. For the source build, I installed locked dependencies and ran npm run build:cached with the repository's pinned Node/npm toolchain. I separately installed the published npm package into a disposable prefix. Both runs used an isolated HOME and cleared client configuration overrides. The commands below use shell variables for disposable paths.

NODE="$(node -p process.execPath)"
WORK="$(mktemp -d /tmp/rea-export.XXXXXX)"
mkdir "$WORK/home" "$WORK/fat32" "$WORK/exfat"
export HOME="$WORK/home"
unset CLAUDE_CONFIG_DIR CODEX_HOME COPILOT_HOME OPENCODE_CONFIG OPENCODE_CONFIG_DIR XDG_CONFIG_HOME GEMINI_CLI_HOME QWEN_HOME GROK_HOME HERMES_HOME HERMES_DATA_DIR_SUFFIX PI_CONFIG_DIR PI_CODING_AGENT_DIR OMP_PROFILE PI_PROFILE SAND_DATA_ROOT KIMI_CODE_HOME QODER_CONFIG_DIR DSH_HOME DSH_AGENTS_HOME
printf '%s\n' '{"artifacts":[],"providers":[],"environments":[],"scenarios":[],"captures":[],"unknowns":[],"records":[]}' > "$WORK/bundle.json"
"$NODE" "$REA_CLI" evidence-import "$WORK/bundle.json" --json
"$NODE" "$REA_CLI" evidence-export "$WORK/bundle.json" "$WORK/apfs.json" --json
hdiutil create -ov -size 64m -fs 'MS-DOS FAT32' -volname REATFAT32 "$WORK/fat32.dmg"
hdiutil attach -nobrowse -mountpoint "$WORK/fat32" -plist "$WORK/fat32.dmg"
diskutil info -plist "$WORK/fat32"
hdiutil create -ov -size 64m -fs ExFAT -volname REATEXFAT "$WORK/exfat.dmg"
hdiutil attach -nobrowse -mountpoint "$WORK/exfat" -plist "$WORK/exfat.dmg"
diskutil info -plist "$WORK/exfat"
for fs in fat32 exfat; do
  for attempt in 1 2 3; do
    "$NODE" "$REA_CLI" evidence-export "$WORK/bundle.json" "$WORK/$fs/output-$attempt.json" --json
  done
  find "$WORK/$fs" -name '.rea-write-*' -print
  "$NODE" "$REA_CLI" evidence-export "$WORK/bundle.json" "$WORK/$fs/overwrite.json" --overwrite --json
done

The import and APFS control each returned exit 0. diskutil info confirmed MS-DOS FAT32 and ExFAT. Each of the six default exports above returned exit 1 and left another staging directory. I repeated these exports with distinct names for the other build on the same images, with the same outcome. After inspection, detach the exact devices reported by hdiutil attach; both detach operations completed with exit 0 in this run. Mountpoint-based detach is also available as hdiutil detach "$WORK/exfat" and hdiutil detach "$WORK/fat32".

Expected behavior

Export should produce the complete canonical bundle under the documented publication and overwrite rules, or return a clear typed destination limitation with completed cleanup of its owned staging resources. The CLI guide accepts a caller-selected local output path and documents preservation of existing destinations without explicit overwrite. The ownership rules separate proof of ownership from readiness. I found no Evidence-export FAT32/exFAT exclusion in these contracts. A filesystem limitation can justify refusing publication; it should not make an ordinary first write leave staging resources behind with a generic permissions-oriented error. The export's atomicity guarantee should remain intact.

Impact and scope

Direct export to either affected filesystem failed in all 12 default attempts across both tested builds and accumulated one staging tree per attempt. Four overwrite attempts also failed. Input and pre-existing destination bytes remained intact. Other operating systems and physical removable drives were not tested.

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: The bounded destination-dependent failure prevents direct export and leaves owned staging files, while the source and existing outputs remain intact.
  • Workaround status: available
  • Workaround basis: Both builds exported successfully to APFS. Exclusive manual copies of those canonical bytes to each affected volume succeeded with byte equality in all four checks. This permits offline transfer; the copy has no verified atomic visibility guarantee and should not run while readers consume the destination.

REA version

Current main source commit d5be402fb3dbfc56ac9854c27b5538df20cc2437, built locally; separately installed npm rea-agents@6.5.0, the latest published release when checked. Both CLI entry points reported 6.5.0.

Runtime and platform

Node v24.18.0; npm 11.16.0; macOS 27.0.1; Darwin arm64. Node/npm match the source checkout's pins, and Node satisfies the published package's engines range.

Target and artifact context

An empty Evidence bundle with all seven manifest/record arrays present and empty, accepted by evidence-import. No provider, executable target, device, or external service was needed. Destinations were 64 MiB FAT32 and exFAT disk images mounted on macOS, with APFS as the successful control.

Interface

CLI

Earliest failure stage

Cleanup, cancellation, or lifecycle

Relevant output or evidence

  • Expected source: docs/cli.md:216-228 describes canonical export and overwrite preservation; docs/mcp-contracts.md:140-141 states complete atomic publication and cancellation cleanup; AGENTS.md:19-21 separates ownership from readiness; docs/tool-design.md:119-123 requires awaited one-shot cleanup and preserved diagnostics. Cancellation was not exercised here. docs/testing.md:266-270 recognizes unsupported-link filesystems and differing fallback visibility, without requiring a streamed fallback for this export.
  • Failure source: Executed CLI commands above on both builds; source call path src/cliEvidenceCommands.ts:31-47 to src/application/JsonFiles.ts:290-329 and src/filesystem/OwnedStagedOutput.ts:48-73,127-134.
  • Evidence provenance: observed
  • Local verification: reproduced
  • Reproduction completeness: complete

Selected output fields below omit disposable paths except for the normalized staging path. Both filesystems and builds returned the same error fields.

{"imported":0,"unknowns_added":0,"total":0}
{"bytes":105,"records":0}
{"code":"cleanup_incomplete","message":"Evidence file could not be accessed. Check file permissions and try again.","details":{"operation":"write","reason":"io","cleanup":"incomplete","cleanup_reason":"Staged file identity changed: <mount>/.rea-write-<id>/content"}}

As a preservation control, I created existing.json containing KEEP EXISTING on each affected volume and exported without overwrite using each build. All four calls returned invalid_request with details.reason: exists and preserved the file's bytes.

A separate Node filesystem probe opened a new file exclusively, sampled descriptor stat, wrote and synced four bytes through that same descriptor, sampled stat again, closed it, and sampled pathname lstat. On each affected filesystem, ino changed between the empty and written file. Final descriptor stat and pathname lstat agreed. The probe did not replace the pathname. This supports the explanation below; the CLI failures used the unmodified runtime.

Typed outcome or recovery expected

Rerun the same fixture on actual FAT32 and exFAT mounts and inspect output, typed outcome, and staging together. A restored outcome is complete canonical output with a truthful publication receipt and no owned staging left behind, or a clear typed unsupported-destination refusal that preserves the absent destination and completes owned cleanup. Keep the existing-destination preservation control and APFS success. Rejection of genuinely replaced staging paths must remain effective. No fix or restoration was tested here.

Analysis or suspected cause

Source-derived reasoning: setFile records dev/ino while the staging file is empty, before serialization. Publication later compares pathname lstat with that initial identity, and cleanup repeats the comparison. The descriptor probe shows a normal inode transition on these macOS-hosted filesystems. The readiness check precedes both the default hard-link publication and overwrite rename, which explains why both modes fail. This does not establish that changing identity sampling alone would fix export. Hard-link availability, atomicity, replacement safety, and AppleDouble companion cleanup still need verification.

Additional context

No known-good REA version was established. The observed scope is limited to the tested macOS-hosted filesystems. This report combines the failed export and retained staging as one caller-visible failure with one reproduction and restoration check.

Originally created by @coygeek on GitHub (Oct 16, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/4613 ### Before submitting - [X] I searched the open and closed issues for a related report. - [X] I removed credentials, tokens, private paths, and sensitive target data. ### Area Evidence, artifacts, or reconstruction ### Observed behavior On macOS, `evidence-export SOURCE OUTPUT --json` fails on mounted FAT32 and exFAT images with exit 1 and `code: cleanup_incomplete`. Each default export leaves the requested destination absent and adds a `.rea-write-*` directory containing `content` and `._content`. This reproduced 3/3 times per filesystem on each tested build. One additional `--overwrite` attempt per filesystem and build failed with the same error. The same source exports successfully on APFS. ### Reproduction Use the `scripts/rea.mjs` entry point from either tested build, assigned to `REA_CLI`, and a supported Node executable assigned to `NODE`. For the source build, I installed locked dependencies and ran `npm run build:cached` with the repository's pinned Node/npm toolchain. I separately installed the published npm package into a disposable prefix. Both runs used an isolated HOME and cleared client configuration overrides. The commands below use shell variables for disposable paths. ```sh NODE="$(node -p process.execPath)" WORK="$(mktemp -d /tmp/rea-export.XXXXXX)" mkdir "$WORK/home" "$WORK/fat32" "$WORK/exfat" export HOME="$WORK/home" unset CLAUDE_CONFIG_DIR CODEX_HOME COPILOT_HOME OPENCODE_CONFIG OPENCODE_CONFIG_DIR XDG_CONFIG_HOME GEMINI_CLI_HOME QWEN_HOME GROK_HOME HERMES_HOME HERMES_DATA_DIR_SUFFIX PI_CONFIG_DIR PI_CODING_AGENT_DIR OMP_PROFILE PI_PROFILE SAND_DATA_ROOT KIMI_CODE_HOME QODER_CONFIG_DIR DSH_HOME DSH_AGENTS_HOME printf '%s\n' '{"artifacts":[],"providers":[],"environments":[],"scenarios":[],"captures":[],"unknowns":[],"records":[]}' > "$WORK/bundle.json" "$NODE" "$REA_CLI" evidence-import "$WORK/bundle.json" --json "$NODE" "$REA_CLI" evidence-export "$WORK/bundle.json" "$WORK/apfs.json" --json hdiutil create -ov -size 64m -fs 'MS-DOS FAT32' -volname REATFAT32 "$WORK/fat32.dmg" hdiutil attach -nobrowse -mountpoint "$WORK/fat32" -plist "$WORK/fat32.dmg" diskutil info -plist "$WORK/fat32" hdiutil create -ov -size 64m -fs ExFAT -volname REATEXFAT "$WORK/exfat.dmg" hdiutil attach -nobrowse -mountpoint "$WORK/exfat" -plist "$WORK/exfat.dmg" diskutil info -plist "$WORK/exfat" for fs in fat32 exfat; do for attempt in 1 2 3; do "$NODE" "$REA_CLI" evidence-export "$WORK/bundle.json" "$WORK/$fs/output-$attempt.json" --json done find "$WORK/$fs" -name '.rea-write-*' -print "$NODE" "$REA_CLI" evidence-export "$WORK/bundle.json" "$WORK/$fs/overwrite.json" --overwrite --json done ``` The import and APFS control each returned exit 0. `diskutil info` confirmed MS-DOS FAT32 and ExFAT. Each of the six default exports above returned exit 1 and left another staging directory. I repeated these exports with distinct names for the other build on the same images, with the same outcome. After inspection, detach the exact devices reported by `hdiutil attach`; both detach operations completed with exit 0 in this run. Mountpoint-based detach is also available as `hdiutil detach "$WORK/exfat"` and `hdiutil detach "$WORK/fat32"`. ### Expected behavior Export should produce the complete canonical bundle under the documented publication and overwrite rules, or return a clear typed destination limitation with completed cleanup of its owned staging resources. The CLI guide accepts a caller-selected local output path and documents preservation of existing destinations without explicit overwrite. The ownership rules separate proof of ownership from readiness. I found no Evidence-export FAT32/exFAT exclusion in these contracts. A filesystem limitation can justify refusing publication; it should not make an ordinary first write leave staging resources behind with a generic permissions-oriented error. The export's atomicity guarantee should remain intact. ### Impact and scope Direct export to either affected filesystem failed in all 12 default attempts across both tested builds and accumulated one staging tree per attempt. Four overwrite attempts also failed. Input and pre-existing destination bytes remained intact. Other operating systems and physical removable drives were not tested. - Impact level: P2 - Assessment status: supported - Impact basis: The bounded destination-dependent failure prevents direct export and leaves owned staging files, while the source and existing outputs remain intact. - Workaround status: available - Workaround basis: Both builds exported successfully to APFS. Exclusive manual copies of those canonical bytes to each affected volume succeeded with byte equality in all four checks. This permits offline transfer; the copy has no verified atomic visibility guarantee and should not run while readers consume the destination. ### REA version Current `main` source commit `d5be402fb3dbfc56ac9854c27b5538df20cc2437`, built locally; separately installed npm `rea-agents@6.5.0`, the latest published release when checked. Both CLI entry points reported `6.5.0`. ### Runtime and platform Node v24.18.0; npm 11.16.0; macOS 27.0.1; Darwin arm64. Node/npm match the source checkout's pins, and Node satisfies the published package's engines range. ### Target and artifact context An empty Evidence bundle with all seven manifest/record arrays present and empty, accepted by `evidence-import`. No provider, executable target, device, or external service was needed. Destinations were 64 MiB FAT32 and exFAT disk images mounted on macOS, with APFS as the successful control. ### Interface CLI ### Earliest failure stage Cleanup, cancellation, or lifecycle ### Relevant output or evidence - Expected source: `docs/cli.md:216-228` describes canonical export and overwrite preservation; `docs/mcp-contracts.md:140-141` states complete atomic publication and cancellation cleanup; `AGENTS.md:19-21` separates ownership from readiness; `docs/tool-design.md:119-123` requires awaited one-shot cleanup and preserved diagnostics. Cancellation was not exercised here. `docs/testing.md:266-270` recognizes unsupported-link filesystems and differing fallback visibility, without requiring a streamed fallback for this export. - Failure source: Executed CLI commands above on both builds; source call path `src/cliEvidenceCommands.ts:31-47` to `src/application/JsonFiles.ts:290-329` and `src/filesystem/OwnedStagedOutput.ts:48-73,127-134`. - Evidence provenance: observed - Local verification: reproduced - Reproduction completeness: complete Selected output fields below omit disposable paths except for the normalized staging path. Both filesystems and builds returned the same error fields. ```json {"imported":0,"unknowns_added":0,"total":0} {"bytes":105,"records":0} {"code":"cleanup_incomplete","message":"Evidence file could not be accessed. Check file permissions and try again.","details":{"operation":"write","reason":"io","cleanup":"incomplete","cleanup_reason":"Staged file identity changed: <mount>/.rea-write-<id>/content"}} ``` As a preservation control, I created `existing.json` containing `KEEP EXISTING` on each affected volume and exported without overwrite using each build. All four calls returned `invalid_request` with `details.reason: exists` and preserved the file's bytes. A separate Node filesystem probe opened a new file exclusively, sampled descriptor stat, wrote and synced four bytes through that same descriptor, sampled stat again, closed it, and sampled pathname lstat. On each affected filesystem, `ino` changed between the empty and written file. Final descriptor stat and pathname lstat agreed. The probe did not replace the pathname. This supports the explanation below; the CLI failures used the unmodified runtime. ### Typed outcome or recovery expected Rerun the same fixture on actual FAT32 and exFAT mounts and inspect output, typed outcome, and staging together. A restored outcome is complete canonical output with a truthful publication receipt and no owned staging left behind, or a clear typed unsupported-destination refusal that preserves the absent destination and completes owned cleanup. Keep the existing-destination preservation control and APFS success. Rejection of genuinely replaced staging paths must remain effective. No fix or restoration was tested here. ### Analysis or suspected cause Source-derived reasoning: `setFile` records dev/ino while the staging file is empty, before serialization. Publication later compares pathname lstat with that initial identity, and cleanup repeats the comparison. The descriptor probe shows a normal inode transition on these macOS-hosted filesystems. The readiness check precedes both the default hard-link publication and overwrite rename, which explains why both modes fail. This does not establish that changing identity sampling alone would fix export. Hard-link availability, atomicity, replacement safety, and AppleDouble companion cleanup still need verification. ### Additional context No known-good REA version was established. The observed scope is limited to the tested macOS-hosted filesystems. This report combines the failed export and retained staging as one caller-visible failure with one reproduction and restoration check.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#2397
No description provided.