terminal_peek discards the real error and then asserts a cause it has not established. A caller reading a pane it is not permitted to read is told the pane may be gone.
In the herdr driver:
herdr pane read "$id" --source "$src" >"$tmp" 2>/dev/null || rc=$?
if [ "$rc" -ne 0 ]; then
[ -s "$tmp" ] && cat "$tmp" >&2 # the error body is a diagnostic, not content
rm -f "$tmp"
echo "herdr: could not read pane '$id' (it may no longer exist)" >&2
Two things combine. The 2>/dev/null drops whatever the terminal wrote to standard error, and the recovery only forwards what arrived on standard output — which covers the terminal's own error JSON but not an OS-level failure. Then the added sentence supplies a cause for every failure alike.
Measured today. A seat running under a sandbox that denies socket operations ran the same read directly and got:
PermissionDenied (Operation not permitted)
Through peek, the same seat saw only "could not read pane 'w1:pC' (it may no longer exist)". It spent time checking whether it had the wrong pane id, the wrong terminal instance, and the wrong argument order — all reasonable, none of them the problem — because the one sentence it was given pointed at absence.
The distinction matters beyond the wasted look. "The pane is gone" and "I am not allowed to look" lead to opposite actions: the first says stop asking and repair the record, the second says the record may be perfectly correct and the caller lacks a permission. This project already treats that distinction as load-bearing elsewhere — terminal_pane_state deliberately returns a separate code with a comment saying the caller must not read it as "closed", and the tmux driver takes only the terminal's own "no server running" sentence as proof of absence, leaving every other failure unknown, because gone is what deletes a placement record and must be earned.
Suggested shape, matching what the rest of the tree already does:
- keep the terminal's standard error rather than discarding it, and forward it alongside whatever arrived on standard output;
- say what happened, not what it might mean — "could not read pane X: <the error>" — and reserve a claim of absence for a reply that actually says the pane is absent;
- where the caller branches on this, give absence and unreadable distinct exit codes so the branch is on the fact, not on parsing a sentence.
Same shape in the same file is worth a sweep while this is open: any place where a failed read is followed by a guess at its cause.
terminal_peekdiscards the real error and then asserts a cause it has not established. A caller reading a pane it is not permitted to read is told the pane may be gone.In the herdr driver:
Two things combine. The
2>/dev/nulldrops whatever the terminal wrote to standard error, and the recovery only forwards what arrived on standard output — which covers the terminal's own error JSON but not an OS-level failure. Then the added sentence supplies a cause for every failure alike.Measured today. A seat running under a sandbox that denies socket operations ran the same read directly and got:
Through
peek, the same seat saw only "could not read pane 'w1:pC' (it may no longer exist)". It spent time checking whether it had the wrong pane id, the wrong terminal instance, and the wrong argument order — all reasonable, none of them the problem — because the one sentence it was given pointed at absence.The distinction matters beyond the wasted look. "The pane is gone" and "I am not allowed to look" lead to opposite actions: the first says stop asking and repair the record, the second says the record may be perfectly correct and the caller lacks a permission. This project already treats that distinction as load-bearing elsewhere —
terminal_pane_statedeliberately returns a separate code with a comment saying the caller must not read it as "closed", and the tmux driver takes only the terminal's own "no server running" sentence as proof of absence, leaving every other failureunknown, because gone is what deletes a placement record and must be earned.Suggested shape, matching what the rest of the tree already does:
Same shape in the same file is worth a sweep while this is open: any place where a failed read is followed by a guess at its cause.