{"record":{"id":"e3210bd64d0f3927","repo":"gastownhall/beads","slug":"building-label-cache-for-team-s-w","errorCode":null,"errorMessage":"building label cache for team %s: %w","messagePattern":"building label cache for team (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/linear/tracker.go","lineNumber":323,"sourceCode":"\t\t}\n\t\tteamCaches[teamID] = cache\n\t}\n\n\t// The primary team's cache is used for creates, which always target the primary team.\n\tprimaryCache := teamCaches[t.teamIDs[0]]\n\tif primaryCache == nil {\n\t\treturn nil, fmt.Errorf(\"building state cache: no cache for primary team %s\", t.teamIDs[0])\n\t}\n\n\tteamLabelCaches := make(map[string]*LabelCache, len(t.teamIDs))\n\tfor _, teamID := range t.teamIDs {\n\t\tteamClient := t.clients[teamID]\n\t\tif teamClient == nil {\n\t\t\tcontinue\n\t\t}\n\t\tlc, err := BuildLabelCache(ctx, teamClient)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"building label cache for team %s: %w\", teamID, err)\n\t\t}\n\t\tteamLabelCaches[teamID] = lc\n\t}\n\tprimaryLabelCache := teamLabelCaches[t.teamIDs[0]]\n\tif primaryLabelCache == nil {\n\t\treturn nil, fmt.Errorf(\"building label cache: no cache for primary team %s\", t.teamIDs[0])\n\t}\n\n\tresult := &tracker.BatchPushResult{}\n\n\tvar toCreate []*types.Issue\n\tvar toUpdate []*types.Issue\n\n\tfor _, issue := range issues {\n\t\textRef := \"\"\n\t\tif issue.ExternalRef != nil {\n\t\t\textRef = *issue.ExternalRef\n\t\t}","sourceCodeStart":305,"sourceCodeEnd":341,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/linear/tracker.go#L305-L341","documentation":"Returned by Tracker.BatchPush when BuildLabelCache fails for one of the configured teams. Per-team label caches are built up front so each update/create maps beads labels to the right team's Linear label IDs; any team's failure aborts the batch.","triggerScenarios":"BatchPush called when the labels query for team %s fails: token lacking read access to that team's labels, network error, rate limit, or the team ID is stale/deleted in Linear.","commonSituations":"Same multi-team drift as the state-cache failure: teams removed in Linear but still configured; API key scope/permission gaps per team; rate limiting when many teams are synced in one batch.","solutions":["Confirm all configured teams exist in Linear and the API key can read their labels","Retry after rate-limit backoff; batch pushes hitting several teams are prone to limits","Prune stale team entries from the beads linear config","Check the wrapped error for the HTTP/GraphQL cause before changing config"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Pre-flight: read labels for each configured team before pushing\nfor _, teamID := range cfg.Linear.TeamIDs {\n    if _, err := fetchLabelsForTeam(ctx, apiKey, teamID); err != nil {\n        return fmt.Errorf(\"labels unreachable for team %s: %w\", teamID, err)\n    }\n}","typeGuard":null,"tryCatchPattern":"result, err := tracker.BatchPush(ctx, issues, forceIDs)\nif err != nil && strings.Contains(err.Error(), \"building label cache for team\") {\n    if isRateLimited(err) {\n        time.Sleep(rateLimitBackoff(err))\n        result, err = tracker.BatchPush(ctx, issues, forceIDs)\n    }\n}","preventionTips":["Space out multi-team batches to avoid label-query rate limits","Keep tokens scoped to read labels on all synced teams","Remove teams from config once deleted from Linear","Honor Linear's rate-limit headers in retry logic"],"tags":["linear","labels","multi-team","api"],"backgroundTag":"linear-api-fetch-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}