fix(perforce): read p4 error records from stdout, not stderr (#9) - #11
Merged
Merged
Conversation
Under -Mj the p4 client writes its error records to STDOUT as JSON. run()
read firstLine(stderr), which is empty in that mode, and fell back to the
process wait status - so every p4 tool failure surfaced as the bare string
"perforce: exit status 1" with no indication of what went wrong.
That has now cost four diagnoses: a missing trust file and an expired
ticket on 2026-09-08, and both failures in the 2026-09-14 Pilot Light
replay. Changelist 118's cause is still unknown for exactly this reason -
it is deterministic and reproduces with the connector idle, and there is
nothing to go on but the exit status.
failureDetail picks the most specific description available, in order:
1. the first -Mj error record on stdout (severity >= 3, p4's "failed"),
2. stderr, which still carries connection-level failures the client
never got far enough to report as a record,
3. the wait status, always available and always useless alone.
Informational records are skipped rather than reported as the cause, so a
"opened for add" line never masquerades as the failure. severity is read
whether p4 renders it as a number or a quoted string, since both occur. A
record carrying data with no severity at all is treated as the error: p4 is
not consistent about emitting it and the text is what the caller needs
either way.
Malformed or truncated JSON degrades to stderr rather than failing. This
runs on a path that is already failing and must never turn one failure into
two.
No change was needed on the audit side: session.go already passes the tool
error's text to finish's detail argument, so the real message now reaches
the local audit log on its own. The broker still receives only the stable
tool_error token - the detail can carry LAN specifics (a server host:port)
and stays on the studio's box by design.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #9.
The bug
Under
-Mjthe p4 client writes its error records to stdout as JSON.run()readfirstLine(stderr)- empty in that mode - and fell back to the process wait status, so every p4 tool failure surfaced as the bare stringperforce: exit status 1with no indication of what went wrong.It has now cost four diagnoses: a missing trust file and an expired ticket on 2026-09-08 (the report on #9), and both failures in the 2026-09-14 Pilot Light replay. Changelist 118 is the live one - it is deterministic, reproduces with the connector idle and the command budget untouched, and its cause is still unknown because there is nothing to go on but an exit status.
The fix
failureDetailpicks the most specific description available, most-specific first:-Mjerror record on stdout (severity >= 3, p4's "failed"),Details that are easy to get wrong and are covered by tests:
//depot/art/tex.png#1 - opened for addmust never be reported as the cause.severityis read whether p4 renders it as a number or a quoted string. Both occur across versions and record types.datarecord with noseverityat all is treated as the error. p4 is not consistent about emitting it, and the text is what the caller needs either way.Audit log
No change was needed.
session.go:399already passes the tool error's text tofinish'sdetailargument, so the real message now reaches the local audit log on its own.Worth being explicit about what this does and does not change for the caller: the broker still receives only the stable
tool_errortoken. The detail can carry LAN specifics - p4 stderr with a serverhost:port- and stays on the studio's box by design. So diagnosing changelist 118 after this ships means reading/data/connector/audit-YYYY-MM-DD.logonbsg-cp-01, not Sentry or the Rails side. That boundary is deliberate and this PR does not move it.Release
This is a prerequisite for the rest of the v0.3.0 work (ButterStack/butter_stack#1904 steps 3 and 4: raise the P-class defaults, and truncate on
max_bytesinstead of erroring). Fixing it first means the re-sync of changelist 118 produces a usable error if it still fails, rather than needing a second release to find out why.Note for anyone following a link here: ButterStack/butter_stack#1904 and #1902 both cite this bug as "#1630", which is wrong -
butter_stack#1630is the connector daemon bring-up issue. This is the real one.Tests
go test ./...green;go vet ./...clean;gofmtclean. Ten cases ininternal/tools/perforce_error_test.go, built from real p4-Mjoutput.