gastownhall/beads · error
building state cache for team %s: %w
Error message
building state cache for team %s: %w
What it means
Returned by Tracker.BatchPush when BuildStateCache fails for one of the configured teams. BatchPush pre-builds per-team workflow state caches so updates resolve state IDs against the correct team; any team's fetch failure aborts the entire batch.
Source
Thrown at internal/linear/tracker.go:304
// Result mapping: batch-create results are matched by title rather than array
// index, since Linear's API does not guarantee response order matches input order.
func (t *Tracker) BatchPush(ctx context.Context, issues []*types.Issue, forceIDs map[string]bool) (*tracker.BatchPushResult, error) {
client := t.primaryClient()
if client == nil {
return nil, fmt.Errorf("no Linear client available")
}
// Build per-team state caches so that updates to issues belonging to different
// teams resolve workflow state IDs against the correct team's state list.
teamCaches := make(map[string]*StateCache, len(t.teamIDs))
for _, teamID := range t.teamIDs {
teamClient := t.clients[teamID]
if teamClient == nil {
continue
}
cache, err := BuildStateCache(ctx, teamClient)
if err != nil {
return nil, fmt.Errorf("building state cache for team %s: %w", teamID, err)
}
teamCaches[teamID] = cache
}
// The primary team's cache is used for creates, which always target the primary team.
primaryCache := teamCaches[t.teamIDs[0]]
if primaryCache == nil {
return nil, fmt.Errorf("building state cache: no cache for primary team %s", t.teamIDs[0])
}
teamLabelCaches := make(map[string]*LabelCache, len(t.teamIDs))
for _, teamID := range t.teamIDs {
teamClient := t.clients[teamID]
if teamClient == nil {
continue
}
lc, err := BuildLabelCache(ctx, teamClient)
if err != nil {View on GitHub (pinned to 71377f2769)
Solutions
- Verify every team ID in the beads linear config still exists in the Linear workspace
- Ensure the API key has access (member/permission) to all configured teams
- Retry — transient rate limits or network errors are common with batch operations
- Remove or fix stale team entries in config so BatchPush skips missing teams
Example fix
// before teamIDs = ["ENG", "OLDTEAM"] // OLDTEAM deleted in Linear // after teamIDs = ["ENG"] // only live teams configured
Defensive patterns
Strategy: validation
Validate before calling
// Verify all configured teams exist and are accessible before pushing
for _, teamID := range cfg.Linear.TeamIDs {
if err := verifyTeamAccessible(ctx, apiKey, teamID); err != nil {
return fmt.Errorf("team %s inaccessible: %w", teamID, err)
}
} Try / catch
result, err := tracker.BatchPush(ctx, issues, forceIDs)
if err != nil {
var teamErr TeamCacheError
if errors.As(err, &teamErr) || strings.Contains(err.Error(), "building state cache for team") {
// retry only the affected team's issues
return retryTeam(teamErr.TeamID, issues)
}
return err
} Prevention
- Prune team IDs deleted from Linear out of beads config
- Scope the API key to all synced teams (or use per-team keys)
- Apply backoff on rate limits before large multi-team batches
- Test multi-team configs with a dry-run push
When it happens
Trigger: BatchPush called when the Linear workflow-states query fails for team %s: expired API key lacking that team's access, rate limiting, network error, or the team ID no longer exists in the Linear workspace.
Common situations: Multi-team setups where a team was deleted/renamed in Linear but remains in beads config; API key issued before the team was added or scoped to other teams; transient API outages during large batch syncs.
Related errors
- building label cache for team %s: %w
- fetching issues from team %s: %w
- finding state for status %s: %w
- loading team labels: %w
- cannot determine Linear team for issue %s
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/ee9a7e4153bc116b.
Report an issue: GitHub.