[GH-ISSUE #4849] [Bug] inspect_plist drops plutil's decode diagnostic on macOS and reports an empty parenthesis #3013

Closed
opened 2026-10-31 00:34:51 +00:00 by bsakaguchi · 2 comments
Owner

Originally created by @apitoagents on GitHub (Oct 17, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/4849

Before submitting

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

Area

Native analysis or decompilation

Observed behavior

On macOS, when inspect_plist selects a file that plutil cannot decode, the error names the cause as an empty parenthesis:

plutil could not decode it as a property list ().

The plutil diagnostic that would explain the refusal (for example Property List error: Unknown character ' ' (0x20) in <integer> on line 3) is dropped. Both the caller-selected path form (invalid_request, "Cannot inspect : …") and the default-plist form (capability_unavailable, "The target has no readable default plist at : …") lose it.

Reproduction

Executed on main 742b6f587, macOS 13.2.1 arm64, Node 24.15.0, source build.

  1. Write a plist whose <integer> text has surrounding whitespace, which Apple's parser rejects:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"><dict><key>v</key><integer> 42 </integer></dict></plist>
  1. node scripts/rea.mjs inspect-plist /absolute/path/bad.plist --json → exit 1, message ends with plutil could not decode it as a property list (). Select another plist in the target with the path input (CLI: --relative-path).
  2. Control: /usr/bin/plutil -convert xml1 -o - -- /absolute/path/bad.plist → exit 1 and prints <path>: Property List error: Unknown character ' ' (0x20) in <integer> on line 3 / JSON error: ….

Reproduced 3/3 with different malformed inputs (whitespace in <integer>, not a property list text, <true>x</true>).

Expected behavior

The error should carry plutil's diagnostic, as 0674facd4 intended ("keep plutil's decode diagnostic"), so the user learns which element and line plutil refused instead of an empty parenthesis.

Impact and scope

Every undecodable plist on macOS. The error class and remediation are unchanged; only the explanatory text is lost. Linux and Windows do not use plutil and are unaffected.

REA version

source commit 742b6f587 (main), package 6.5.0

Runtime and platform

Node 24.15.0; macOS 13.2.1 arm64; /usr/bin/plutil from that macOS

Target and artifact context

Self-authored malformed XML plists; no third-party target

Interface

CLI

Earliest failure stage

Transport or result projection

Relevant output or evidence

Spawning the exact command REA runs from Node (plutil -convert xml1 -o - -- <file>, LC_ALL=C LANG=C) gives status: 1, stderr: "", and the whole diagnostic on stdout. The same holds for -convert json -o -, for -o /some/file and for plutil -lint: on this macOS, plutil writes decode diagnostics to stdout, not stderr.

Typed outcome or recovery expected

Same codes as today (invalid_request for a selected path, capability_unavailable for a missing default), with the plutil text inside the parenthesis.

Analysis or suspected cause

Source-derived: plutilDiagnostic in src/native/NativeMacOSProvider.ts reads only xml.value.stderr. plutil reports the failure on the stream selected by -o, so with -o - the text is on stdout and stderr is empty. Reading stderr first and falling back to stdout, with the path prefix stripped as today, restores the message. Whether newer macOS releases moved the diagnostic to stderr was not checked; reading both streams covers either.

Additional context

A second, separate observation while reproducing: when the target itself is a malformed plist file (inspect-plist /path/to/bad.plist, no --relative-path), the file is the caller's selection, yet the failure is reported as capability_unavailable with "The target has no readable default plist … Select another plist in the target". Whether that should become the caller-selection input error is a contract question for the maintainers; it is not part of this report's fix.

Originally created by @apitoagents on GitHub (Oct 17, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/4849 ### 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 Native analysis or decompilation ### Observed behavior On macOS, when `inspect_plist` selects a file that `plutil` cannot decode, the error names the cause as an empty parenthesis: ``` plutil could not decode it as a property list (). ``` The plutil diagnostic that would explain the refusal (for example `Property List error: Unknown character ' ' (0x20) in <integer> on line 3`) is dropped. Both the caller-selected path form (`invalid_request`, "Cannot inspect <path>: …") and the default-plist form (`capability_unavailable`, "The target has no readable default plist at <path>: …") lose it. ### Reproduction Executed on main `742b6f587`, macOS 13.2.1 arm64, Node 24.15.0, source build. 1. Write a plist whose `<integer>` text has surrounding whitespace, which Apple's parser rejects: ```xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"><dict><key>v</key><integer> 42 </integer></dict></plist> ``` 2. `node scripts/rea.mjs inspect-plist /absolute/path/bad.plist --json` → exit 1, `message` ends with `plutil could not decode it as a property list (). Select another plist in the target with the path input (CLI: --relative-path).` 3. Control: `/usr/bin/plutil -convert xml1 -o - -- /absolute/path/bad.plist` → exit 1 and prints `<path>: Property List error: Unknown character ' ' (0x20) in <integer> on line 3 / JSON error: …`. Reproduced 3/3 with different malformed inputs (whitespace in `<integer>`, `not a property list` text, `<true>x</true>`). ### Expected behavior The error should carry plutil's diagnostic, as `0674facd4` intended ("keep plutil's decode diagnostic"), so the user learns which element and line plutil refused instead of an empty parenthesis. ### Impact and scope Every undecodable plist on macOS. The error class and remediation are unchanged; only the explanatory text is lost. Linux and Windows do not use plutil and are unaffected. ### REA version source commit 742b6f587 (main), package 6.5.0 ### Runtime and platform Node 24.15.0; macOS 13.2.1 arm64; /usr/bin/plutil from that macOS ### Target and artifact context Self-authored malformed XML plists; no third-party target ### Interface CLI ### Earliest failure stage Transport or result projection ### Relevant output or evidence Spawning the exact command REA runs from Node (`plutil -convert xml1 -o - -- <file>`, `LC_ALL=C LANG=C`) gives `status: 1`, `stderr: ""`, and the whole diagnostic on **stdout**. The same holds for `-convert json -o -`, for `-o /some/file` and for `plutil -lint`: on this macOS, plutil writes decode diagnostics to stdout, not stderr. ### Typed outcome or recovery expected Same codes as today (`invalid_request` for a selected path, `capability_unavailable` for a missing default), with the plutil text inside the parenthesis. ### Analysis or suspected cause Source-derived: `plutilDiagnostic` in `src/native/NativeMacOSProvider.ts` reads only `xml.value.stderr`. plutil reports the failure on the stream selected by `-o`, so with `-o -` the text is on stdout and stderr is empty. Reading stderr first and falling back to stdout, with the path prefix stripped as today, restores the message. Whether newer macOS releases moved the diagnostic to stderr was not checked; reading both streams covers either. ### Additional context A second, separate observation while reproducing: when the target itself is a malformed plist file (`inspect-plist /path/to/bad.plist`, no `--relative-path`), the file is the caller's selection, yet the failure is reported as `capability_unavailable` with "The target has no readable default plist … Select another plist in the target". Whether that should become the caller-selection input error is a contract question for the maintainers; it is not part of this report's fix.
Author
Owner

@matbrgz commented on GitHub (Oct 17, 2026):

Cross-referencing with sibling plist and XML deserializer reports #3371, #3373, #4042, #4714, and #4852. All isolate defects across macOS plutil error projection and inspect_plist XML decoder normalization.

Consolidated under Cluster K (XML Keyed-Archive Deserialization & CDATA Handling / Plist Decoder) in tracker #4330.

<!-- gh-comment-id:6242161407 --> @matbrgz commented on GitHub (Oct 17, 2026): Cross-referencing with sibling plist and XML deserializer reports #3371, #3373, #4042, #4714, and #4852. All isolate defects across macOS `plutil` error projection and `inspect_plist` XML decoder normalization. Consolidated under **Cluster K (XML Keyed-Archive Deserialization & CDATA Handling / Plist Decoder)** in tracker #4330.
Author
Owner

@morluto commented on GitHub (Oct 18, 2026):

Fixed on main in 7ca7e3871, adapting @apitoagents's PR #4851 with coauthor credit.

The failure projection now reads both plutil streams, reuses the native failure-capture bounds before building the error, and retains raw excerpts with observed byte totals, exit status and truncation. Only display text loses its repeated exact path prefix. Empty output receives a useful no-diagnostic reason. New CLI/MCP and provider-boundary regressions cover stream placement and evidence preservation.

Executed on macOS26.3 arm64 / Node24.18:

  • Controlled baseline:6 failures, including stdout-only producing the original empty parenthesis; stderr-only lacked raw output, both-stream output lost one diagnostic, empty output lacked a useful reason.
  • Real CLI baseline:3 new checks failed because raw output was omitted. This Mac writes those diagnostic messages to stderr, unlike the older report's stdout placement; no false claim of reproducing that OS behavior here.
  • Final focused selection:46 passed across6files, including real CLI/MCP selected/default malformed-input refusals, existing valid plist/format/precision/prototype-key checks and cleanup regressions.
  • Oversized controlled UTF8 output verifies64KiB per-stream excerpts, original observed byte counts, truncation and serialization bounds; no heavy CPU test.
  • check:fast, docs:check5, discovery1565 files exactly once, normal commit/pre-push hooks passed. Two read-only source reviews found no blocker.

The separate selected-target classification observation in this report was reproduced through the public CLI and is tracked in #4923. It remains open; this diagnostic fix does not decide that selection contract.

Limits: macOS26.3 native behavior only; stdout-only and dual-stream placement are controlled boundary observations, not executions on older macOS. No Linux/Windows run, performance improvement or full-codebase completion claim. CPU/process-family memory not measured; error capacity is validated by explicit output bounds. Heavy CPU test/profiling remain stopped.

<!-- gh-comment-id:6243507592 --> @morluto commented on GitHub (Oct 18, 2026): Fixed on main in 7ca7e3871, adapting @apitoagents's PR #4851 with coauthor credit. The failure projection now reads both plutil streams, reuses the native failure-capture bounds before building the error, and retains raw excerpts with observed byte totals, exit status and truncation. Only display text loses its repeated exact path prefix. Empty output receives a useful no-diagnostic reason. New CLI/MCP and provider-boundary regressions cover stream placement and evidence preservation. Executed on macOS26.3 arm64 / Node24.18: - Controlled baseline:6 failures, including stdout-only producing the original empty parenthesis; stderr-only lacked raw output, both-stream output lost one diagnostic, empty output lacked a useful reason. - Real CLI baseline:3 new checks failed because raw output was omitted. This Mac writes those diagnostic messages to stderr, unlike the older report's stdout placement; no false claim of reproducing that OS behavior here. - Final focused selection:46 passed across6files, including real CLI/MCP selected/default malformed-input refusals, existing valid plist/format/precision/prototype-key checks and cleanup regressions. - Oversized controlled UTF8 output verifies64KiB per-stream excerpts, original observed byte counts, truncation and serialization bounds; no heavy CPU test. - check:fast, docs:check5, discovery1565 files exactly once, normal commit/pre-push hooks passed. Two read-only source reviews found no blocker. The separate selected-target classification observation in this report was reproduced through the public CLI and is tracked in #4923. It remains open; this diagnostic fix does not decide that selection contract. Limits: macOS26.3 native behavior only; stdout-only and dual-stream placement are controlled boundary observations, not executions on older macOS. No Linux/Windows run, performance improvement or full-codebase completion claim. CPU/process-family memory not measured; error capacity is validated by explicit output bounds. Heavy CPU test/profiling remain stopped.
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#3013
No description provided.