{"record":{"id":"c6eedf1fe9ac60ba","repo":"gastownhall/beads","slug":"gitlab-epic-milestone-sync-v","errorCode":null,"errorMessage":"GitLab epic milestone sync: %v","messagePattern":"GitLab epic milestone sync: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"cmd/bd/gitlab.go","lineNumber":698,"sourceCode":"\t\t}\n\t\tfor _, err := range res.Errors {\n\t\t\twarn(fmt.Sprintf(\"GitLab dependency link sync: %v\", err))\n\t\t}\n\t}\n\n\tif len(linkData.ScopedIssues) > 0 {\n\t\tcount, errs := gt.PushEpicMilestones(ctx, linkData.ScopedIssues, gitlab.EpicMilestoneOptions{\n\t\t\tDryRun: dryRun,\n\t\t\tOnPlan: func(issueID string, issueIID int, milestoneID int) {\n\t\t\t\tif !jsonOutput {\n\t\t\t\t\t_, _ = fmt.Fprintf(out, \"  [dry-run] Would set GitLab milestone %d on %s (#%d)\\n\",\n\t\t\t\t\t\tmilestoneID, issueID, issueIID)\n\t\t\t\t}\n\t\t\t},\n\t\t})\n\t\tmilestonesUpdated = count\n\t\tfor _, err := range errs {\n\t\t\twarn(fmt.Sprintf(\"GitLab epic milestone sync: %v\", err))\n\t\t}\n\t}\n\n\treturn linksPushed, linksLicenseSkipped, milestonesUpdated\n}\n\ntype gitlabLinkSyncData struct {\n\tScopedIssues []*types.Issue\n\tDesiredLinks []gitlab.DependencyLink\n}\n\nfunc collectGitLabLinkSyncData(ctx context.Context, st storage.Storage, opts tracker.SyncOptions) (gitlabLinkSyncData, []string) {\n\tif st == nil {\n\t\treturn gitlabLinkSyncData{}, []string{\"GitLab dependency link sync skipped: database not available\"}\n\t}\n\n\tallIssues, err := st.SearchIssues(ctx, \"\", types.IssueFilter{})\n\tif err != nil {","sourceCodeStart":680,"sourceCodeEnd":716,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/gitlab.go#L680-L716","documentation":"This warning is emitted by pushGitLabDependencyLinks (cmd/bd/gitlab.go:698) after gt.PushEpicMilestones completes with per-issue errors. During `bd gitlab sync` or `bd gitlab push`, beads tries to repair epic-child milestones on GitLab (set the GitLab milestone matching the epic on each scoped issue). Any error the GitLab API or local lookup returns for a milestone update is collected in the returned `errs` slice and printed as this warning; the sync continues rather than aborting.","triggerScenarios":"Running `bd gitlab sync` or `bd gitlab push` with scoped issues whose epic milestone needs repairing, and PushEpicMilestones fails for one or more issues — e.g. the GitLab API rejects the milestone update (milestone ID no longer exists, milestone belongs to a different group/project), the epic or child issue was deleted remotely, the token lacks write scope, or an epic-child/milestone lookup returns 404.","commonSituations":"GitLab tier downgrades or moving epics between groups so the recorded milestone ID is invalid; stale milestone IDs after remote cleanup; expired/insufficiently-scoped PAT; milestone deleted in GitLab while beads still references it; network/5xx errors during the milestone PUT.","solutions":["Re-run `bd gitlab sync` to see the full underlying error in the warning and check GitLab project/group access with the configured token.","Verify the milestone referenced by the epic still exists in GitLab (or re-link the epic so a fresh milestone ID is fetched).","Confirm the token has api write scope and the epic/child issue are in a project the token can modify.","If a GitLab tier changed (Premium features lost), restore the tier or stop expecting epic-milestone repair for those links.","Re-run the sync after fixing; the pass is additive/resumable, so previously succeeded issues are skipped."],"exampleFix":"// before: stale milestone recorded on the epic, GitLab returns 400\nGitLab epic milestone sync: PUT https://gitlab.com/api/v4/projects/42/issues/17: 400 {\"milestone\":[{\"error\":\"does not exist\"}]}\n// after: recreate/repair milestone in GitLab (or re-link epic) then re-run\nbd gitlab sync\nGitLab epic milestone sync: no errors (milestone 9 set on bd-123 (#17))","handlingStrategy":"fallback","validationCode":"// before bd gitlab push/sync\ngitlab_project=$(git remote get-url origin | sed 's#.*gitlab.com[:/]##; s#\\.git$##')\ncurl -sf -H \"PRIVATE-TOKEN: $GITLAB_TOKEN\" \"https://gitlab.com/api/v4/projects/$(python3 -c 'import urllib.parse,sys;print(urllib.parse.quote(sys.argv[1],safe=\"\"))' \"$gitlab_project\")\" >/dev/null || echo 'GitLab project/token not writable — fix before pushing links/milestones'","typeGuard":null,"tryCatchPattern":"// callers of pushGitLabDependencyLinks cannot catch per-issue errors (they are warned, not returned);\n// treat milestonesUpdated < expected as the failure signal:\nif milestonesUpdated < expected {\n    fmt.Fprintf(os.Stderr, \"epic milestone sync incomplete: %d/%d; re-run bd gitlab sync after fixing GitLab access\\n\", milestonesUpdated, expected)\n}","preventionTips":["Keep the GitLab PAT api-scoped and rotate before expiry.","Re-link epics after moving them between groups/projects so recorded milestone IDs stay valid.","Run `bd gitlab sync --dry-run` first to surface milestone failures without writes.","Watch for GitLab tier changes that alter epic/milestone behavior."],"tags":["gitlab","milestone","api-error","sync"],"backgroundTag":"gitlab-api-sync-failure","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}