Gitea merge refusals show only "HTTP 405" when T3 talks to Gitea through tea#32241

opened6d agobyRRJWadley

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): the tea path reads only the HTTP status from stderr and builds a generic Forgejo API request failed (HTTP 405). detail. Gitea's JSON body in result.stdout, which holds the reason (for example The head branch is behind the base branch), is dropped.
  • The fj path already includes it: ForgejoCli.ts:385-387 appends body.text. That came from the fix for #11568, which covered only fj. main is unchanged on both paths as of 2026-11-06.
  • Gitea returns 405 from POST /repos/{owner}/{repo}/pulls/{index}/merge for many unrelated reasons: an out-of-date branch, required checks, approvals, WIP, a disallowed merge style, signing, and others (go-gitea/gitea v28.0.0 routers/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

  1. Self-hosted Gitea (seen on 28.0.0). Log in with tea only (no fj login for that host), and add the repo as a T3 project.
  2. On the base branch, add a protection rule with "Block merge if pull request is outdated" turned on.
  3. Open a PR, then push a new commit to the base branch so the PR falls behind.
  4. In T3, open the PR and press Merge.
  5. 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

shell
1Server trace, ws.rpc.pullRequests.runAction -> ForgejoCli.api:
2
3
4PullRequestOperationError: Pull request operation runAction failed: Forgejo API request failed (HTTP 405).
5 at ForgejoCli.api
6 at PullRequestService.runActionAndInvalidate
7
8
9The span has no response body because the `tea` path never keeps it.
  • #11568: same symptom on the fj path; that fix didn't cover tea
  • #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)