gastownhall/beads · error

cannot determine Linear team for issue %s

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.

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)

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.