Checklist:
Describe the bug
GitlabProvider.listBranches treats 404 as no branches inside its pagination loop and returns an empty slice with a nil error that throws away every branch already collected from earlier pages.
for {
gitlabBranches, resp, err := g.client.Branches.ListBranches(repo.RepositoryId, opt)
// 404s are not an error here, just a normal false.
if resp != nil && resp.StatusCode == http.StatusNotFound {
return []gitlab.Branch{}, nil
}
if err != nil {
return nil, err
}
for _, gitlabBranch := range gitlabBranches {
branches = append(branches, *gitlabBranch)
}
if resp.NextPage == 0 {
break
}
opt.Page = resp.NextPage
}
return branches, nil
}
This matters because the appset reconciler trusts nil error completely. A returned error is safe however, a successful empty result flows straight through to prune step so a repo with more than 100 branches that has 404s on page 2 can have all of its generated apps deleted rather than the generator failing and retrying. Interestingly, GitLab returns 404 rather than 403 for a project the token can't see so a token whose project access is revoked in the middle can hit this.
To Reproduce
This is a race against the GitLab API, so the deterministic reproduction is at the unit level.
- Stand up an
httptest server for /api/v4/projects/{id}/repository/branches that returns
a full page of 100 branches with X-Next-Page: 2, then 404 for page=2.
- Call
listBranches with allBranches: true.
- Observe it returns
([], nil) - the 100 branches from page 1 are gone, and there is no error.
Expected behavior
A 404 encountered while paginating should be returned as an error, so the reconciler records an ErrorOccurred condition and requeues with existing Applications untouched.
Screenshots
N/A
Version
argocd: v3.5.1+109ca7c
BuildDate: 2026-08-12T11:51:48Z
GitCommit: 109ca7ca71139e514114499d294a492e7910a965
GitTreeState: clean
GoVersion: go1.26.4
Compiler: gc
Platform: darwin/arm64
argocd-server: v3.5.1
BuildDate: 2026-08-12T11:28:06Z
GitCommit: 109ca7ca71139e514114499d294a492e7910a965
GitTreeState: clean
GitTag: v3.5.1
GoVersion: go1.26.4
Compiler: gc
Platform: linux/arm64
Kustomize Version: v5.8.1 2026-02-09T16:15:27Z
Helm Version: v4.2.1+gd591a19
Kubectl Version: v0.36.1
Jsonnet Version: v0.22.0
Logs
Paste any relevant application logs here.
Checklist:
argocd version.Describe the bug
GitlabProvider.listBranches treats 404 as no branches inside its pagination loop and returns an empty slice with a nil error that throws away every branch already collected from earlier pages.
This matters because the appset reconciler trusts nil error completely. A returned error is safe however, a successful empty result flows straight through to prune step so a repo with more than 100 branches that has 404s on page 2 can have all of its generated apps deleted rather than the generator failing and retrying. Interestingly, GitLab returns 404 rather than 403 for a project the token can't see so a token whose project access is revoked in the middle can hit this.
To Reproduce
This is a race against the GitLab API, so the deterministic reproduction is at the unit level.
httptestserver for/api/v4/projects/{id}/repository/branchesthat returnsa full page of 100 branches with
X-Next-Page: 2, then404forpage=2.listBrancheswithallBranches: true.([], nil)- the 100 branches from page 1 are gone, and there is no error.Expected behavior
A 404 encountered while paginating should be returned as an error, so the reconciler records an
ErrorOccurredcondition and requeues with existing Applications untouched.Screenshots
N/A
Version
Logs