gastownhall/beads · error
no Linear client available
Error message
no Linear client available
What it means
fetchIssueAcrossTeams needs a Linear client to resolve an issue identifier when the tracker has at most one team configured. When t.primaryClient() is nil — the tracker was constructed without an API client — there is no way to fetch anything, so it returns this error instead of panicking.
Source
Thrown at internal/linear/parent_reconcile.go:26
// fetchIssueAcrossTeams locates an issue by its Linear identifier across
// all configured team clients. Single-team setups hit the primary client
// directly; multi-team setups fall through each client in order until one
// returns a non-nil issue. Returns (issue, hostClient, nil) on success;
// the hostClient is the team's client that owned the issue and should be
// reused for subsequent mutations (so the update path doesn't re-probe
// and silently fall back to the wrong client on transient probe errors —
// see clientForExternalID's fallback behavior in tracker.go).
//
// Returns (nil, nil, nil) when no team has the issue.
func (t *Tracker) fetchIssueAcrossTeams(ctx context.Context, identifier string) (*Issue, *Client, error) {
if identifier == "" {
return nil, nil, nil
}
if len(t.teamIDs) <= 1 {
client := t.primaryClient()
if client == nil {
return nil, nil, errors.New("no Linear client available")
}
li, err := client.FetchIssueByIdentifier(ctx, identifier)
if err != nil {
return nil, nil, err
}
if li == nil {
return nil, nil, nil
}
return li, client, nil
}
// Multi-team: try each client. First non-nil result wins. Rate-limit
// errors abort immediately (the cross-team probe shouldn't burn
// through quota when the circuit breaker has already tripped).
for _, teamID := range t.teamIDs {
client := t.clients[teamID]
if client == nil {
continue
}View on GitHub (pinned to 71377f2769)
Solutions
- Initialize the Tracker with a valid Linear API client (API key from the environment/config) before reconciling
- If this instance is not supposed to talk to Linear, skip reconciliation when primaryClient() == nil instead of calling it
- For multi-team setups, ensure the per-team clients map is populated so the multi-team path is used
Example fix
// before
tracker := &linear.Tracker{}
stats, err := tracker.ReconcileParents(ctx, links, false)
// after
if tracker.PrimaryClient() == nil {
return errors.New("linear: cannot reconcile parents without a client; set the API key")
}
stats, err := tracker.ReconcileParents(ctx, links, false) Defensive patterns
Strategy: type-guard
Validate before calling
if tracker.PrimaryClient() == nil {
return errors.New("linear client not configured; set the Linear API key")
} Type guard
func (t *Tracker) HasClient() bool { return t.PrimaryClient() != nil } Try / catch
li, _, err := fetchIssueAcrossTeams(ctx, tracker, identifier)
if err != nil && err.Error() == "no Linear client available" {
return nil, fmt.Errorf("linear: configure an API client before fetching %s", identifier)
} Prevention
- Verify the Linear API key is present in the environment before starting reconciliation jobs
- Construct the Tracker through one factory that requires a non-nil client
- In dry-run/CI contexts, skip linear work explicitly instead of constructing a client-less Tracker
When it happens
Trigger: Calling fetchIssueAcrossTeams (via parent-link reconciliation flows) on a Tracker whose primaryClient() returns nil: no Linear API key/client was provided at construction, or the multi-team path was taken with a single teamID and no primary client.
Common situations: Running parent reconciliation in an environment without LINEAR_API_KEY configured; constructing linear.Tracker with zero-value options in tests or partial setups; a config migration dropping the client field.
Related errors
- database not available: %w
- Linear authentication not configured Options: OAuth (for C
- no Linear team ID configured Run: bd config set linear.team_
- invalid Linear team ID (expected UUID format like '12345678-
- Linear team ID not configured
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/72f4ea072e8a2f5d.
Report an issue: GitHub.