e574c38 fix(releases): create releases only for tags present in Gitea; stop sending target (#331)
📊 Changes
1 file changed (+37 additions, -4 deletions)
View changed files
📝src/lib/gitea.ts (+37 -4)
📄 Description
Follow-up to #332. That PR fixed idempotent asset reconciliation, but the reporter in #331 was actually blocked one step earlier: release creation itself was 404ing, so no release (and no assets) ever existed.
Root cause
The create payload always sent target: release.target_commitish (e.g. "main"). When a release's git tag isn't yet present in the Gitea mirror — which happens when Gitea's own git-mirror clone lags behind this metadata sync — Gitea tries to create the tag from target:
if the target ref can't be resolved → generic 404 "The target couldn't be found" and the release is lost,
if it can → it would create a new tag at the wrong commit.
"The target couldn't be found." is Gitea's generic error.not_found (a bare ctx.NotFound()), confirming the failure is at git/tag resolution time, not the request itself.
Reproduction effort
I reproduced the reporter's exact stack on a test VM — Forgejo 15 rootless + read_only: true + cap_drop: ALL + user 99:100 — and also tested Gitea 1.20/1.21/1.23/1.24/1.25/1.26 and Forgejo 1.21/11/12. On a healthy repo every version creates the release fine (with target); the 404 only occurs when the tag is absent at create time. This matches the field symptom: all releases failing while issues/PRs (which need no git refs) succeed.
Fix
Verify the git tag exists in Gitea before creating a release. If it isn't synced yet, skip it (logged) and let a later sync create it once the mirror has the tag — never create a tag via target.
Drop target from the create and update payloads. For a mirror the tag comes from upstream git, so Gitea attaches the release to the existing tag; target is unnecessary and is the thing that triggers the 404 (the documented workaround for the go-gitea/gitea#21681 / forgejo#1196 class).
Surface skipped-missing-tag releases in the summary log.
Verification (real mirror function on a Forgejo instance)
Tag present → release created withouttarget → 201, assets uploaded ✅
Tag removed (simulating sync lag) → skipped cleanly, no 404, no bogus tag, retried next sync ✅
bun test src/lib/gitea-releases.test.ts → 15/15 pass
Note: the underlying why the tag was momentarily missing on the reporter's box is still being confirmed via their Forgejo server logs, but this change removes the failure mode regardless (and is the correct behavior for a mirror).
🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.
## 📋 Pull Request Information
**Original PR:** https://github.com/RayLabsHQ/gitea-mirror/pull/333
**Author:** [@arunavo4](https://github.com/arunavo4)
**Created:** 6/24/2026
**Status:** ✅ Merged
**Merged:** 6/24/2026
**Merged by:** [@arunavo4](https://github.com/arunavo4)
**Base:** `main` ← **Head:** `fix/release-create-target-404-331`
---
### 📝 Commits (1)
- [`e574c38`](https://github.com/RayLabsHQ/gitea-mirror/commit/e574c383b507c7284b3bd7dc7ce296b0d9c36f82) fix(releases): create releases only for tags present in Gitea; stop sending target (#331)
### 📊 Changes
**1 file changed** (+37 additions, -4 deletions)
<details>
<summary>View changed files</summary>
📝 `src/lib/gitea.ts` (+37 -4)
</details>
### 📄 Description
Follow-up to #332. That PR fixed idempotent **asset** reconciliation, but the reporter in #331 was actually blocked one step earlier: release **creation** itself was 404ing, so no release (and no assets) ever existed.
## Root cause
The create payload always sent `target: release.target_commitish` (e.g. `"main"`). When a release's git **tag isn't yet present** in the Gitea mirror — which happens when Gitea's own git-mirror clone lags behind this metadata sync — Gitea tries to *create* the tag from `target`:
- if the target ref can't be resolved → generic **`404 "The target couldn't be found"`** and the release is lost,
- if it can → it would create a **new tag at the wrong commit**.
`"The target couldn't be found."` is Gitea's generic `error.not_found` (a bare `ctx.NotFound()`), confirming the failure is at git/tag resolution time, not the request itself.
## Reproduction effort
I reproduced the reporter's **exact stack** on a test VM — Forgejo **15 rootless** + `read_only: true` + `cap_drop: ALL` + `user 99:100` — and also tested Gitea 1.20/1.21/1.23/1.24/1.25/1.26 and Forgejo 1.21/11/12. On a healthy repo **every version creates the release fine** (with `target`); the 404 only occurs when the **tag is absent at create time**. This matches the field symptom: all releases failing while issues/PRs (which need no git refs) succeed.
## Fix
- **Verify the git tag exists in Gitea before creating a release.** If it isn't synced yet, skip it (logged) and let a later sync create it once the mirror has the tag — never create a tag via `target`.
- **Drop `target` from the create and update payloads.** For a mirror the tag comes from upstream git, so Gitea attaches the release to the existing tag; `target` is unnecessary and is the thing that triggers the 404 (the documented workaround for the go-gitea/gitea#21681 / forgejo#1196 class).
- **Surface skipped-missing-tag releases** in the summary log.
## Verification (real mirror function on a Forgejo instance)
- Tag present → release created **without** `target` → 201, assets uploaded ✅
- Tag removed (simulating sync lag) → **skipped cleanly**, no 404, no bogus tag, retried next sync ✅
- `bun test src/lib/gitea-releases.test.ts` → 15/15 pass
Note: the underlying *why the tag was momentarily missing* on the reporter's box is still being confirmed via their Forgejo server logs, but this change removes the failure mode regardless (and is the correct behavior for a mirror).
---
<sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
📋 Pull Request Information
Original PR: https://github.com/RayLabsHQ/gitea-mirror/pull/333
Author: @arunavo4
Created: 6/24/2026
Status: ✅ Merged
Merged: 6/24/2026
Merged by: @arunavo4
Base:
main← Head:fix/release-create-target-404-331📝 Commits (1)
e574c38fix(releases): create releases only for tags present in Gitea; stop sending target (#331)📊 Changes
1 file changed (+37 additions, -4 deletions)
View changed files
📝
src/lib/gitea.ts(+37 -4)📄 Description
Follow-up to #332. That PR fixed idempotent asset reconciliation, but the reporter in #331 was actually blocked one step earlier: release creation itself was 404ing, so no release (and no assets) ever existed.
Root cause
The create payload always sent
target: release.target_commitish(e.g."main"). When a release's git tag isn't yet present in the Gitea mirror — which happens when Gitea's own git-mirror clone lags behind this metadata sync — Gitea tries to create the tag fromtarget:404 "The target couldn't be found"and the release is lost,"The target couldn't be found."is Gitea's genericerror.not_found(a barectx.NotFound()), confirming the failure is at git/tag resolution time, not the request itself.Reproduction effort
I reproduced the reporter's exact stack on a test VM — Forgejo 15 rootless +
read_only: true+cap_drop: ALL+user 99:100— and also tested Gitea 1.20/1.21/1.23/1.24/1.25/1.26 and Forgejo 1.21/11/12. On a healthy repo every version creates the release fine (withtarget); the 404 only occurs when the tag is absent at create time. This matches the field symptom: all releases failing while issues/PRs (which need no git refs) succeed.Fix
target.targetfrom the create and update payloads. For a mirror the tag comes from upstream git, so Gitea attaches the release to the existing tag;targetis unnecessary and is the thing that triggers the 404 (the documented workaround for the go-gitea/gitea#21681 / forgejo#1196 class).Verification (real mirror function on a Forgejo instance)
target→ 201, assets uploaded ✅bun test src/lib/gitea-releases.test.ts→ 15/15 passNote: the underlying why the tag was momentarily missing on the reporter's box is still being confirmed via their Forgejo server logs, but this change removes the failure mode regardless (and is the correct behavior for a mirror).
🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.