Skip to content

Fix off-by-one in RPC result attribution - #1

Draft
plan9better wants to merge 1 commit into
masterfrom
fix-response-offset
Draft

Fix off-by-one in RPC result attribution#1
plan9better wants to merge 1 commit into
masterfrom
fix-response-offset

Conversation

@plan9better

Copy link
Copy Markdown
Collaborator

Problem

configure() prepends "configure terminal" to the payload, so the device returns N+1 results for an N-line patch. summary() zipped those results against the patch commands starting at index 0, so every command was reported with the previous command's result.

Two visible consequences:

  • Failures are blamed on the following command.
  • The first real command absorbs the prologue's empty result and always logs msg="ok", so a failed command can be reported as successful.

zip() silently truncates on the length mismatch, which is why this never surfaced as an error.

Impact

Real log from a failing run against a switch, with a 14-line patch:

result cmd="no channel-group 3"                msg="ok"
result cmd="exit"  error_code=-3004  "the string does not match any command in current mode"
result cmd="port-channel 3 lacp-mode dynamic"  msg="ok"
result cmd="interface eth-0-3"  error_code=-1000  "% Can't set lacp-mode, agg3 have existed"

This reads as nonsense: exit cannot produce -3004 in interface mode, and interface eth-0-3 cannot produce a lacp-mode error. Shift by one and it resolves exactly:

reported against actually belongs to
exit no channel-group 3 (invalid syntax — the real bug, in the patch)
interface eth-0-3 port-channel 3 lacp-mode dynamic
exit mlag 3

The misattribution actively obscured the root cause: no channel-group 3 was logged msg="ok" while it was in fact the command that failed and stalled the whole patch.

Fix

  • configure() drops the prologue's result entry, so what it returns aligns 1:1 with commands. The prepend stays an implementation detail of the function that introduced it.
  • summary() bails on a length mismatch instead of letting zip() truncate silently, so this class of misalignment fails loudly.

Testing

cargo build clean. Pre-existing cargo fmt diff and 3 clippy warnings are untouched on master and left alone to keep the diff reviewable.

Verified by hand against the log above: with the prologue dropped, all 14 results line up with their commands and each error lands on a command that can actually produce it.

🤖 Generated with Claude Code

configure() prepends "configure terminal" to the payload, so the device
returns one more result than the caller's command list. summary() zipped
those results against the patch commands from index 0, so every command
was reported with the previous command's result: failures were logged
against the following command, and the first real command absorbed the
prologue's empty result and always printed msg="ok".

Drop the prologue's entry in configure() so the returned results align
1:1 with the commands, and bail in summary() on a length mismatch rather
than let zip() silently truncate.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant