fix(forgejo): tea API failures keep Gitea's reason#32244

merged4
main
fix/issue-32241
opened6d agobyJJulius Marminge
View on GitHubTwo-way sync enabledPR last imported 4h ago

Problem

When T3 talks to Gitea or Forgejo through tea (no fj login matches the host), a refused API call fails with only Forgejo API request failed (HTTP 405).. Gitea returns 405 from the merge endpoint for many reasons: an out-of-date branch, required approvals, a disallowed merge style, and others. Pressing Merge therefore gives nothing to act on. tea api --include exits 0, writes the status line to stderr, and writes Gitea's JSON body, which holds the reason, to stdout. T3 read only stderr.

Reproduced against a real Gitea 28.3.0 (docker) with tea 0.16.0. The base branch had a protection rule that blocks outdated pull requests, the PR was behind its base, and Merge sent POST /repos/admin1/demo/pulls/1/merge. T3 reported Forgejo API request failed (HTTP 405)., while tea's stdout held {"message":"The head branch is behind the base branch",...}. A second variant (merge commits disallowed on the repository) was dropped the same way.

Change

In ForgejoCli.api's tea path, the generic HTTP failure detail now carries the response body: Forgejo API request failed (HTTP <status>): <body>. This matches what the fj path (requestFj) already does. The fixed messages for 401/403, 404 and 429, the "without an HTTP status" message, httpStatus and reason are unchanged. An empty body keeps the old text.

Scope and approval

Fixes pingdotgg/t3code#32241

Verification

  • New test keeps Gitea's failure reason when tea reports an HTTP error in packages/source-control-forgejo/src/server/ForgejoSourceControlProvider.test.ts. It feeds the real tea output shape for a 405 merge refusal. It fails on main with expected 'Forgejo API request failed (HTTP 405).' to include 'The head branch is behind the base br…' and passes with the fix.
  • vp test run packages/source-control-forgejo: 6 files, 64 tests pass. vp run check:changed: format, lint, knip, typecheck (11 packages) and the 2 changed test files (44 tests) pass.
  • Runtime transcript: T3's ForgejoCli.api driving the real tea 0.16.0 against a real Gitea 28.3.0, with a synthetic repository and user.
Runtime transcript (before d5fc29fe5c, after cbf7fa5873)
1Scenario: base branch protection block_on_outdated_branch=true, PR #1 behind base;
2T3 sends POST pulls/1/merge {Do: merge, delete_branch_after_merge: true}
3
4## Raw tea output (exit 0)
5stderr: HTTP/1.1 405 Method Not Allowed
6stdout: {"message":"The head branch is behind the base branch","url":"http://127.0.0.1:39241/api/swagger"}
7
8## Before: main d5fc29fe5c
9$ tea api --include --login gitea-local --repo admin1/demo --method POST --data @- http://127.0.0.1:39241/api/v1/repos/admin1/demo/pulls/1/merge -> exit 0
10{ "tag": "ForgejoCliError", "command": "tea", "httpStatus": 405,
11 "detail": "Forgejo API request failed (HTTP 405)." }
12
13## Before, variant: merge commits disallowed (main d5fc29fe5c)
14tea stdout: {"message":"merge is not allowed an allowed merge style for this repository","url":"http://127.0.0.1:39241/api/swagger"}
15{ "tag": "ForgejoCliError", "command": "tea", "httpStatus": 405,
16 "detail": "Forgejo API request failed (HTTP 405)." }
17
18## After: fix/issue-32241 cbf7fa5873
19{ "tag": "ForgejoCliError", "command": "tea", "httpStatus": 405,
20 "detail": "Forgejo API request failed (HTTP 405): {\"message\":\"The head branch is behind the base branch\",\"url\":\"http://127.0.0.1:39241/api/swagger\"}" }

Server-only change with no new render path: the merge toast already shows this detail string, so there are no before/after captures. Not checked: a Forgejo server (only Gitea was run), and the toast in a client.

AI-assisted; verified with the tests and tools listed above.

Checks

1 failing · 21 successful · 20 skipped
Some checks failed

Failing1

36cb74e

Added 2 commits

  • test(forgejo): reproduce #32241, tea HTTP errors drop Gitea's reason33a692d
  • fix(forgejo): tea API failures keep Gitea's reasoncbf7fa5
Synced from GitHub

Approvability

Verdict: Approved at cbf7fa5

Macroscope's review found this PR approvable — Generic tea HTTP failures now retain the server’s response text, making failures more actionable. Request behavior and existing special-case error messages remain unchanged.

No code changes detected at 36cb74e. Prior analysis still applies.

You can add or adjust custom eligibility rules. Learn more.

Synced from GitHub

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ProviderMetricMain baselineThis PRImpactPR ceiling
CodexTotal thread wire5.0 KiB5.0 KiB0 B (0.0%)6.8 KiB✅
CodexThread snapshot wire3.8 KiB3.8 KiB0 B (0.0%)4.9 KiB✅
CodexLive turn WebSocket wire1.2 KiB1.2 KiB0 B (0.0%)2.0 KiB✅
CodexLive turn WebSocket decoded21.5 KiB21.5 KiB0 B (0.0%)24.4 KiB✅
CodexLive turn messages110 (0.0%)8✅
ClaudeTotal thread wire5.0 KiB5.0 KiB0 B (0.0%)6.8 KiB✅
ClaudeThread snapshot wire3.8 KiB3.8 KiB0 B (0.0%)4.9 KiB✅
ClaudeLive turn WebSocket wire1.2 KiB1.2 KiB0 B (0.0%)2.0 KiB✅
ClaudeLive turn WebSocket decoded21.9 KiB21.9 KiB0 B (0.0%)24.4 KiB✅
ClaudeLive turn messages110 (0.0%)8✅

Other scenarios

All 64 scenario measurements match main.

All 64 scenario measurements
ProviderScenarioMetricMain baselineThis PRImpactPR ceiling
Codexshort turn resumeWebSocket decoded10.5 KiB10.5 KiB0 B (0.0%)12.7 KiB✅
Codexshort turn resumeWebSocket messages220 (0.0%)2✅
Codexlong turn running, cold openHTTP decoded281.7 KiB281.7 KiB0 B (0.0%)302.7 KiB✅
Codexlong turn running, cold openrows75750 (0.0%)75✅
Codexlong turn running, history pageHTTP decoded92.4 KiB92.4 KiB0 B (0.0%)107.4 KiB✅
Codexlong turn running, history pagerows75750 (0.0%)75✅
Codexlong turn finished, cold openHTTP decoded381.5 KiB381.5 KiB0 B (0.0%)410.2 KiB✅
Codexlong turn finished, cold openrows1511510 (0.0%)151✅
Codexlong turn finished, socket cold openWebSocket decoded381.6 KiB381.6 KiB0 B (0.0%)410.2 KiB✅
Codexlong turn finished, socket cold openWebSocket messages220 (0.0%)2✅
Codexlong turn finished, socket cold openrows1511510 (0.0%)151✅
Codexlong turn finished, work pageHTTP decoded186.2 KiB186.2 KiB0 B (0.0%)200.2 KiB✅
Codexlong turn finished, work pagerows1501500 (0.0%)150✅
Codexidle thread resumeWebSocket decoded65 B65 B0 B (0.0%)200 B✅
Codexidle thread resumeWebSocket messages110 (0.0%)1✅
Codexfleet shell snapshotHTTP decoded42.4 KiB42.4 KiB0 B (0.0%)53.7 KiB✅
Codexfleet shell snapshotrows27270 (0.0%)27✅
Codexfleet shell subscribeWebSocket decoded65 B65 B0 B (0.0%)200 B✅
Codexfleet shell subscribeWebSocket messages110 (0.0%)1✅
Codexfleet shell resume checkHTTP decoded18 B18 B0 B (0.0%)100 B✅
Codexfleet shell resume checkrows0000✅
Codexstale shell reloadHTTP decoded42.4 KiB42.4 KiB0 B (0.0%)53.7 KiB✅
Codexstale shell reloadrows27270 (0.0%)27✅
Codexstale shell resumeWebSocket decoded140 B140 B0 B (0.0%)400 B✅
Codexstale shell resumeWebSocket messages220 (0.0%)2✅
Codexstale todo list cold openHTTP decoded72.4 KiB72.4 KiB0 B (0.0%)83.0 KiB✅
Codexstale todo list cold openrows75750 (0.0%)75✅
Codexstale todo list cold opennodes220 (0.0%)4✅
Codexstale todo list cold opensubagents220 (0.0%)4✅
Codexstale todo list cold openplans110 (0.0%)2✅
Codexstale todo list cold openproviderTurns110 (0.0%)2✅
Codexstale todo list cold opencheckpoints110 (0.0%)2✅
Claudeshort turn resumeWebSocket decoded10.6 KiB10.6 KiB0 B (0.0%)12.7 KiB✅
Claudeshort turn resumeWebSocket messages220 (0.0%)2✅
Claudelong turn running, cold openHTTP decoded283.3 KiB283.3 KiB0 B (0.0%)302.7 KiB✅
Claudelong turn running, cold openrows75750 (0.0%)75✅
Claudelong turn running, history pageHTTP decoded93.4 KiB93.4 KiB0 B (0.0%)107.4 KiB✅
Claudelong turn running, history pagerows75750 (0.0%)75✅
Claudelong turn finished, cold openHTTP decoded384.2 KiB384.2 KiB0 B (0.0%)410.2 KiB✅
Claudelong turn finished, cold openrows1511510 (0.0%)151✅
Claudelong turn finished, socket cold openWebSocket decoded384.3 KiB384.3 KiB0 B (0.0%)410.2 KiB✅
Claudelong turn finished, socket cold openWebSocket messages220 (0.0%)2✅
Claudelong turn finished, socket cold openrows1511510 (0.0%)151✅
Claudelong turn finished, work pageHTTP decoded188.1 KiB188.1 KiB0 B (0.0%)200.2 KiB✅
Claudelong turn finished, work pagerows1501500 (0.0%)150✅
Claudeidle thread resumeWebSocket decoded66 B66 B0 B (0.0%)200 B✅
Claudeidle thread resumeWebSocket messages110 (0.0%)1✅
Claudefleet shell snapshotHTTP decoded43.0 KiB43.0 KiB0 B (0.0%)53.7 KiB✅
Claudefleet shell snapshotrows27270 (0.0%)27✅
Claudefleet shell subscribeWebSocket decoded66 B66 B0 B (0.0%)200 B✅
Claudefleet shell subscribeWebSocket messages110 (0.0%)1✅
Claudefleet shell resume checkHTTP decoded18 B18 B0 B (0.0%)100 B✅
Claudefleet shell resume checkrows0000✅
Claudestale shell reloadHTTP decoded43.0 KiB43.0 KiB0 B (0.0%)53.7 KiB✅
Claudestale shell reloadrows27270 (0.0%)27✅
Claudestale shell resumeWebSocket decoded140 B140 B0 B (0.0%)400 B✅
Claudestale shell resumeWebSocket messages220 (0.0%)2✅
Claudestale todo list cold openHTTP decoded73.4 KiB73.4 KiB0 B (0.0%)83.0 KiB✅
Claudestale todo list cold openrows75750 (0.0%)75✅
Claudestale todo list cold opennodes220 (0.0%)4✅
Claudestale todo list cold opensubagents220 (0.0%)4✅
Claudestale todo list cold openplans110 (0.0%)2✅
Claudestale todo list cold openproviderTurns110 (0.0%)2✅
Claudestale todo list cold opencheckpoints110 (0.0%)2✅

Baseline: fb2e5a0 · PR result: 36cb74e · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 108.5 KiB
  • Claude decoded thread snapshot: 108.8 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

Synced from GitHub

Explain Change →

No actionable comments were generated in the recent review.

Walkthrough

The tea API error handler now includes a trimmed, nonempty response body in the error detail when an HTTP status is present. A regression test checks that an HTTP 405 merge failure includes the status and server-provided message.

Changes: tea API error details

Layer / File(s)Summary
Preserve response details in API failures
packages/source-control-forgejo/src/server/ForgejoCli.ts, packages/source-control-forgejo/src/server/ForgejoSourceControlProvider.test.ts
The tea error handler distinguishes missing status, status with a response body, and status without a body. The regression test verifies that an HTTP 405 failure includes the server message.

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix · Severity of issue fixed: Medium

Suggested reviewers: mnigos

Merge Risk: External image Minimal · up to 12c07

Tea merge failures can now show the server’s reason. No material merge-blocking risk was identified.

Pre-merge checks: Passed 4

Check nameStatusExplanation
Title checkPassedThe title clearly identifies the Forgejo fix: preserving Gitea’s reason in tea API failures.
Description checkPassedThe description covers the problem, change, scope, and focused verification. It also states that Forgejo and client-toast behavior were not checked.
Linked Issues checkPassed[#32241] ForgejoCli.api now trims tea stdout and includes a non-empty response body in generic HTTP failure details. The added regression test verifies that a 405 merge refusal preserves both the …
Out of Scope Changes checkPassedThe whole-PR diff changes ForgejoCli.api error details and adds a regression test for [#32241]. It contains no demonstrated unrelated change. The separate merge-protection awareness work in #28194 r…
Full details: Linked Issues check

Explanation

[#32241] ForgejoCli.api now trims tea stdout and includes a non-empty response body in generic HTTP failure details. The added regression test verifies that a 405 merge refusal preserves both the status and Gitea’s branch-behind reason. The existing special status messages and empty-body text remain unchanged.

Full details: Out of Scope Changes check

Explanation

The whole-PR diff changes ForgejoCli.api error details and adds a regression test for [#32241]. It contains no demonstrated unrelated change. The separate merge-protection awareness work in #28194 remains outside this PR.

Finishing Touches

Generate docstrings

  • Commit to this branch
  • Create a new PR
Generate unit tests (beta)

  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Usage-based review receipt

Note

This review was completed with usage-based billing: files reviewed beyond your plan's included limits are billed at $0.25/file. View usage-based billing.

External image Recent review info

External image Run configuration

External image Commits

Reviewing files that changed from the base of the PR and between cbf7fa58734ed7c138a45d24c3feebe94352aa7e and 12c07073d0d3365004cbaf0e94a8d67af48f0ecd.

External image Files selected for processing: 1

  • packages/source-control-forgejo/src/server/ForgejoSourceControlProvider.test.ts

Limit details: You’ve used all 10 included reviews currently available.

Added 1 commit

  • Merge origin/main into fix/issue-3224112c0707

Evidence

Create a synthetic PR, advance its base branch, and enforce outdated-branch protection for the merging account. Open the same PR in each isolated app and press Merge.

Both captures: Linux web dev app, fresh database per commit, Gitea 1.25.1, tea 0.16.0 without matching fj credentials, light theme, 1280×800 CSS viewport.

BeforeAfter
f86e21f7cc12c07073d0
Merge recordingMerge recording
Reports HTTP 405 alone.Reports HTTP 405 plus JSON containing “The head branch is behind the base branch”.

Driver · Seed · Environment

AI-assisted; verified with the tests and tools listed above.

Not shown:

  • Remote freshness of origin/main: fetch attempts returned HTTP 503, including a main-only fetch. The base is the computed merge-base with the locally available origin/main.
  • Desktop and mobile presentation were not exercised; evidence covers the real web client and server.
  • Empty-body and missing-status failures, other HTTP statuses, and the accompanying GitHub request-policy changes were not exercised; the reproduction covered Gitea's actual outdated-branch HTTP 405 refusal.

Added 1 commit

  • Merge branch 'main' into fix/issue-3224136cb74e