Gitea merge refusals show only "HTTP 405" when T3 talks to Gitea through tea#32241
What happened
Pressing Merge in T3 on a pull request on a self-hosted Gitea fails every time with a toast that only says Forgejo API request failed (HTTP 405). Gitea doesn't say why it refused, so the toast gives nothing to act on. In the captured failure, Gitea had refused because a branch protection rule requires the head branch to be up to date and the PR was behind its base.
Diagnosis
T3 throws away Gitea's error message when it calls the API through tea, which is the path used when no fj login matches the host.
packages/source-control-forgejo/src/server/ForgejoCli.ts:700-723(v0.1.7): theteapath reads only the HTTP status from stderr and builds a genericForgejo API request failed (HTTP 405).detail. Gitea's JSON body inresult.stdout, which holds the reason (for exampleThe head branch is behind the base branch), is dropped.- The
fjpath already includes it:ForgejoCli.ts:385-387appendsbody.text. That came from the fix for #11568, which covered onlyfj.mainis unchanged on both paths as of 2026-11-06. - Gitea returns 405 from
POST /repos/{owner}/{repo}/pulls/{index}/mergefor many unrelated reasons: an out-of-date branch, required checks, approvals, WIP, a disallowed merge style, signing, and others (go-gitea/gitea v28.0.0routers/api/v1/repo/pull.go:962-980,services/pull/merge.go:660-705). Without the body, these can't be told apart.
The captured failure: Gitea 28.0.0, user is a repo admin, base branch protection has 0 required approvals, status checks *, "block on outdated branch" on and block_admin_merge_override: true. The PR head was 27 commits behind base, and the PR's checks were green. T3 sent {Do: "merge", delete_branch_after_merge: true} (ForgejoPullRequestProvider.ts:517-524). Because the body was dropped, the exact Gitea message isn't in the logs. The outdated-branch refusal is inferred from the protection settings and the PR's ancestry.
Related but separate: T3 offers Merge as the primary action even though Gitea's protection blocks it. The Forgejo provider doesn't read Gitea's protection blockers (see #28194).
Steps to reproduce
- Self-hosted Gitea (seen on 28.0.0). Log in with
teaonly (nofjlogin for that host), and add the repo as a T3 project. - On the base branch, add a protection rule with "Block merge if pull request is outdated" turned on.
- Open a PR, then push a new commit to the base branch so the PR falls behind.
- In T3, open the PR and press Merge.
- The toast says
Forgejo API request failed (HTTP 405).and nothing else. The same request through the API returns Gitea's reason in the JSON body.
Version
T3 Code 0.1.7 (server; the failure's stack trace matches the running build). Also present in v0.1.6 and on main as of 2026-11-06.
Environment
T3 Code server 0.1.7 on Linux (WSL2), used from the T3 Code desktop app on macOS. Self-hosted Gitea 28.0.0, accessed through tea.
Evidence
| 1 | Server trace, ws.rpc.pullRequests.runAction -> ForgejoCli.api: |
| 2 | |
| 3 | |
| 4 | PullRequestOperationError: Pull request operation runAction failed: Forgejo API request failed (HTTP 405). |
| 5 | at ForgejoCli.api |
| 6 | at PullRequestService.runActionAndInvalidate |
| 7 | |
| 8 | |
| 9 | The span has no response body because the `tea` path never keeps it. |
Related issues
- #11568: same symptom on the
fjpath; that fix didn't covertea - #18755: Forgejo/Gitea merge fails for repo admins on approval-protected branches (closed, not planned)
- #28194: Forgejo PRs carry no checks state
- #30491: Gitea and Forgejo pull requests have no auto-merge option
Fix applied or workaround
Press Update branch (in T3 or Gitea), wait for checks, then merge. Or merge from Gitea's web UI, which shows why it's blocked.
Filed by
Claude Opus 5.5 (Claude Code) via T3 Code triage, with investigation by GPT-6.1 Sol (Codex)