gastownhall/beads · error

no Linear client available

Error message

no Linear client available

What it means

CreateIssue requires the primary team's client (the first entry in teamIDs, via primaryClient). If the tracker has no team IDs configured or the primary client was never created, it returns this error instead of attempting a GraphQL mutation. Like Validate, it is a lifecycle guard indicating the tracker was not properly initialized.

Source

Thrown at internal/linear/tracker.go:188

		if client == nil {
			continue
		}
		li, err := client.FetchIssueByIdentifier(ctx, identifier)
		if err != nil {
			continue // Issue might belong to a different team.
		}
		if li != nil {
			ti := linearToTrackerIssue(li)
			return &ti, nil
		}
	}
	return nil, nil
}

func (t *Tracker) CreateIssue(ctx context.Context, issue *types.Issue) (*tracker.TrackerIssue, error) {
	client := t.primaryClient()
	if client == nil {
		return nil, fmt.Errorf("no Linear client available")
	}

	priority := PriorityToLinear(issue.Priority, t.config)

	stateID, err := t.findStateID(ctx, client, issue.Status)
	if err != nil {
		return nil, fmt.Errorf("finding state for status %s: %w", issue.Status, err)
	}

	labelCache, err := BuildLabelCache(ctx, client)
	if err != nil {
		return nil, fmt.Errorf("loading team labels: %w", err)
	}
	labelIDs, 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)
	}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Call Init successfully (credentials AND team ID configured) before CreateIssue, and check its error
  2. Configure a primary team (linear.team_id or --team) so primaryClient() resolves
  3. Call tracker.Validate() before the sync/push loop to fail fast with a clearer message
  4. In tests, use the tracker registry and full Init flow rather than a bare &Tracker{}

Example fix

// before
tracker := &linear.Tracker{}
created, err := tracker.CreateIssue(ctx, issue) // "no Linear client available"
// after
tracker := &linear.Tracker{}
tracker.SetTeamIDs([]string{"ENG"})
if err := tracker.Init(ctx, store); err != nil { return err }
created, err := tracker.CreateIssue(ctx, issue)
Defensive patterns

Strategy: validation

Validate before calling

// Guard lifecycle before creates
tracker := &linear.Tracker{}
if err := tracker.Init(ctx, store); err != nil { return err }
if err := tracker.Validate(); err != nil { return err } // fails if clients map empty

Try / catch

created, err := tr.CreateIssue(ctx, issue)
if err != nil {
    if strings.Contains(err.Error(), "no Linear client available") {
        return fmt.Errorf("tracker not initialized; call Init with team/credentials first: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling CreateIssue on a Tracker where Init was never called, Init failed before clients were built (e.g. missing team ID), or teamIDs is empty while clients map is nil/empty.

Common situations: Calling CreateIssue before Init in custom tooling; Init failing at the team-ID check (see error 1946) and the caller proceeding anyway; constructing a zero-value Tracker in tests.

Related errors


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