gastownhall/beads · error

cannot determine Linear team for issue

Error message

cannot determine Linear team for issue %s

What it means

Returned by Tracker.UpdateIssue when clientForExternalID cannot determine which Linear team a previously-synced issue belongs to, based on its external ID. The Linear client is per-team, so without a team mapping the update cannot be routed and is aborted.

Solutions

  1. Confirm the externalID actually came from this tracker (a Linear issue identifier) and matches the current config
  2. Re-add the missing Linear team to the tracker's team configuration so clientForExternalID can route the ID
  3. Re-fetch/re-sync the issue to obtain a fresh, correctly-formatted external ID
  4. Fall back to CreateIssue for the issue if it genuinely has no Linear counterpart

Example fix

// before
tracker.UpdateIssue(ctx, "ENG-9999", issue) // team ENG not configured
// after
tracker.UpdateIssue(ctx, "BEADS-1234", issue) // externalID from a configured team
Defensive patterns

Strategy: validation

Validate before calling

// Only update issues whose external ID was produced by this tracker for a configured team
if !strings.Contains(externalID, primaryTeamKeyPrefix) && !knownTeamExternalID(externalID) {
    return fmt.Errorf("external ID %q does not belong to any configured Linear team", externalID)
}

Type guard

func knownExternalID(id string) bool {
    team, _, ok := splitLinearExternalID(id)
    return ok && configuredTeams[team]
}

Try / catch

updated, err := tracker.UpdateIssue(ctx, externalID, issue)
if err != nil && strings.Contains(err.Error(), "cannot determine Linear team") {
    // fall back to creating a fresh Linear issue instead
    updated, err = tracker.CreateIssue(ctx, issue)
}

Prevention

When it happens

Trigger: Tracker.UpdateIssue called with an externalID that does not correspond to any configured team (malformed/foreign external ID, or the team that owned the issue was removed from t.teamIDs config).

Common situations: Issues synced under an older multi-team config whose identifier format changed; stale external IDs after reconfiguring Linear teams; copying issues between databases; externalID pointing at a Linear team no longer listed in beads config.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/efd5ed9954502a67. Report an issue: GitHub.

Appendix: source

Thrown at internal/linear/tracker.go:243

		}
		ti := linearToTrackerIssue(created)
		return &ti, nil
	}

	created, err := client.CreateIssue(ctx, issue.Title, description, priority, stateID, labelIDs)
	if err != nil {
		return nil, err
	}

	ti := linearToTrackerIssue(created)
	return &ti, nil
}

func (t *Tracker) UpdateIssue(ctx context.Context, externalID string, issue *types.Issue) (*tracker.TrackerIssue, error) {
	// Route to the correct team's client based on the external ID.
	client := t.clientForExternalID(ctx, externalID)
	if client == nil {
		return nil, fmt.Errorf("cannot determine Linear team for issue %s", externalID)
	}

	labelCache, err := BuildLabelCache(ctx, client)
	if err != nil {
		return nil, fmt.Errorf("loading team labels: %w", err)
	}
	_, unknown := ResolveLabelIDs(issue, labelCache, t.config)
	for _, name := range unknown {
		fmt.Fprintf(os.Stderr, "linear: bead %s: label %q not found on Linear team (skipped)\n", issue.ID, name)
	}
	mapper := &linearFieldMapper{config: t.config, labelCache: labelCache}
	updates := mapper.IssueToTracker(issue)

	// Resolve and include state so status changes are pushed to Linear.
	stateID, err := t.findStateID(ctx, client, issue.Status)
	if err != nil {
		return nil, fmt.Errorf("finding state for status %s: %w", issue.Status, err)
	}

View on GitHub (pinned to 71377f2769)