# git-remote-entire: relay helper-status before send-pack exit check

`ad52a59`→[main](/content/gh/entireio/cli/commits/main/index.html)·

pjbgf·1mo ago·1 file·+17 added/-10 removed

send-pack exits non-zero on per-ref rejections (D/F conflict, protected
branch, server hook decline). The buffered \`error refs/X <reason>\` lines
are what git's transport-helper.c reads to render the user-visible
\`! \[remote rejected\]\` output. Forwarding helper-status only on the happy
path meant Wait/feed errors swallowed those lines, leaving users with
only \`send-pack exited with error: exit status 1\`.

Relay helper-status before checking exit codes so the real reason
reaches the user. Examples now surfaced:

### Branch protection violation
! \[remote rejected\] branch-test -> branch-test (upstream: push declined due to repository rule violations)
error: failed to push some refs to 'X'

### Invalid ref:
! \[remote rejected\] test-prefix -> test-prefix (ref name conflicts with an existing ref namespace)
error: failed to push some refs to 'X'

### Permissions:
! \[remote rejected\] new-test/repo -> new-test/repo (pushing to GitHub: tracing transport: handshake: http transport: authorization failed: unexpected requesting "https://github.com/x/x/info/refs?service=git-receive-pack" status code: 403: Permission to x/x.git denied to <user>.)
error: failed to push some refs to 'X'

Assisted-by: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: Paulo Gomes <paulo@entire.io>

## Sessions

25d3ab26f571View transcript

### Fix Git Remote Helper Status Relay

Claude Code·Opus 4.7·1 step

## Changes

1

- internal/remotehelper/githelper

- Mpush.go+17/-10

```go
150 unmodified lines

151
152
153
154
155
156
157
158
159
160
161
162
163
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179

150 unmodified lines

\t\treturn fmt.Errorf("reading helper-status: %w", err)
\t}\n
\tif err := <-feedErr; err != nil {\n\t\tif waitErr := sp.Wait(); waitErr != nil {\n\t\t\treturn errors.Join(err, fmt.Errorf("send-pack exited after feeder error: %w", waitErr))\n\t\t}\n\t\treturn err\n\t}\n\tif err := sp.Wait(); err != nil {\n\t\treturn fmt.Errorf("send-pack exited with error: %w", err)\n\t}\n
\n\t// Relay helper-status before checking exit codes. send-pack exits\n\t// non-zero on per-ref rejections (D/F conflict, protected branch,\n\t// hook decline); the buffered `error refs/X <reason>` lines are\n\t// what git's transport-helper.c reads to print `! [remote rejected]\n\t// refs/X (<reason>)`. Returning early on Wait/feed errors swallowed\n\t// them, leaving users with only `send-pack exited with error: exit\n\t// status 1`.
\tif _, err := stdout.Write(helperStatus); err != nil {\n\t\treturn fmt.Errorf("writing helper-status: %w", err)\n\t}\n\tif _, err := fmt.Fprintln(stdout); err != nil {\n\t\treturn fmt.Errorf("writing push terminator: %w", err)\n\t}\n
\tif err := <-feedErr; err != nil {\n\t\tif waitErr := sp.Wait(); waitErr != nil {\n\t\t\treturn errors.Join(err, fmt.Errorf("send-pack exited after feeder error: %w", waitErr))\n\t\t}\n\t\treturn err\n\t}\n\tif err := sp.Wait(); err != nil {\n\t\treturn fmt.Errorf("send-pack exited with error: %w", err)\n\t}\n\treturn nil\n}\n```

Minternal/remotehelper/githelper/push.go+17/-10
