fix(api): answer 409 when repository files already exist on disk (#40095) (#40098)

Backport #40095 by @hktitof

when a repository directory exists on disk without a database row, for
example after a failed delete, POST /user/repos answered 500 with an
empty message

the migrate endpoint already answers 409 for this error and the fork API
got the same treatment in https://github.com/go-gitea/gitea/pull/39909,
so the create handler now maps ErrRepoFilesAlreadyExist to 409 too. this
covers POST /user/repos, POST /orgs/{org}/repos and the admin user route
since they all go through CreateUserRepo

the integration test creates the leftover directory and asserts 409, on
master it gets 500. runs on sqlite as usual

this also touches routers/api/v1/repo/repo.go, which several open PRs
modify, but none of them changes this error mapping

Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Signed-off-by: hktitof <hktitof@users.noreply.github.com>
Co-authored-by: Abdellatif Anaflous <62770500+hktitof@users.noreply.github.com>
Co-authored-by: hktitof <hktitof@users.noreply.github.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
authored and GitHub committed 2026-10-30 22:26:30 +01:00
1 parent 51736a9706
commit 3162dc4cbf
3 files changed
+4 -15

No files matched your search

+1 -1
View File
@@ -77,7 +77,7 @@ func IsErrRepoAlreadyExist(err error) bool {
}
func (err ErrRepoAlreadyExist) Error() string {
return fmt.Sprintf("repository already exists [uname: %s, name: %s]", err.Uname, err.Name)
return fmt.Sprintf("repository already exists [owner: %s, repo: %s]", err.Uname, err.Name)
}
func (err ErrRepoAlreadyExist) Unwrap() error {
+1 -12
View File
@@ -23,7 +23,6 @@ import (
unit_model "gitea.dev/models/unit"
user_model "gitea.dev/models/user"
"gitea.dev/modules/git"
"gitea.dev/modules/label"
"gitea.dev/modules/log"
"gitea.dev/modules/markup"
"gitea.dev/modules/optional"
@@ -255,17 +254,7 @@ func CreateUserRepo(ctx *context.APIContext, owner *user_model.User, opt api.Cre
ObjectFormatName: string(opt.ObjectFormatName),
})
if err != nil {
if repo_model.IsErrRepoAlreadyExist(err) {
ctx.APIError(http.StatusConflict, "The repository with the same name already exists.")
} else if db.IsErrNameReserved(err) ||
db.IsErrNamePatternNotAllowed(err) ||
label.IsErrTemplateLoad(err) {
ctx.APIError(http.StatusUnprocessableEntity, err.Error())
} else if errors.Is(err, util.ErrPermissionDenied) {
ctx.APIError(http.StatusForbidden, err.Error())
} else {
ctx.APIErrorInternal(err)
}
ctx.APIErrorAuto(err)
return
}
+2 -2
View File
@@ -423,7 +423,7 @@ func testAPIRepoMigrateConflict(t *testing.T, u *url.URL) {
AddTokenAuth(httpContext.Token)
resp := httpContext.Session.MakeRequest(t, req, http.StatusConflict)
respJSON := DecodeJSON(t, resp, map[string]string{})
assert.Equal(t, "The repository with the same name already exists.", respJSON["message"])
assert.Contains(t, respJSON["message"], "already exists")
})
}
@@ -500,7 +500,7 @@ func testAPIRepoCreateConflict(t *testing.T, u *url.URL) {
AddTokenAuth(httpContext.Token)
resp := httpContext.Session.MakeRequest(t, req, http.StatusConflict)
respJSON := DecodeJSON(t, resp, map[string]string{})
assert.Equal(t, "The repository with the same name already exists.", respJSON["message"])
assert.Contains(t, respJSON["message"], "already exists")
})
}