gastownhall/beads · critical

no linear client

Error message

no linear client

What it means

BuildLabelCache requires a non-nil *Client to call GetTeamLabels. This guard error fires immediately when a nil client is passed, before any network call is made. It indicates a wiring/initialization bug in the caller, not a Linear API problem.

Source

Thrown at internal/linear/mapping.go:485

	case types.StatusBlocked:
		return "started" // Linear doesn't have blocked state type
	case types.StatusClosed:
		return "completed"
	default:
		return "unstarted"
	}
}

// LabelCache maps normalized Linear label names (lowercase) to Linear label IDs
// for a single team.
type LabelCache struct {
	IDByLowerName map[string]string
}

// BuildLabelCache fetches team labels and indexes them by lowercase trimmed name.
func BuildLabelCache(ctx context.Context, client *Client) (*LabelCache, error) {
	if client == nil {
		return nil, fmt.Errorf("no linear client")
	}
	labels, err := client.GetTeamLabels(ctx)
	if err != nil {
		return nil, err
	}
	c := &LabelCache{
		IDByLowerName: make(map[string]string, len(labels)),
	}
	for _, lb := range labels {
		k := strings.ToLower(strings.TrimSpace(lb.Name))
		if k != "" && lb.ID != "" {
			c.IDByLowerName[k] = lb.ID
		}
	}
	return c, nil
}

// IssueTypeToLinearLabelLookupKey returns the label_type_map key (lowercase) for

View on GitHub (pinned to 71377f2769)

Solutions

  1. Initialize the Linear client before calling BuildLabelCache (e.g. linear.NewClient(token)) and check its constructor error.
  2. Add a nil check on the client at the call site (CreateIssue/UpdateIssue/BatchPush) and fail fast with a clear config error.
  3. Fix any constructor path that can return (nil, nil) so callers never hold a nil client.
  4. In tests, pass a stubbed *Client or test the cache-building logic via an interface.

Example fix

// before
cache, err := linear.BuildLabelCache(ctx, client) // client is nil
// after
if client == nil {
    return fmt.Errorf("linear client not initialized; check config/token loading")
}
cache, err := linear.BuildLabelCache(ctx, client)
Defensive patterns

Strategy: validation

Validate before calling

if client == nil {
    return fmt.Errorf("linear client not initialized")
}
cache, err := linear.BuildLabelCache(ctx, client)

Type guard

func hasLinearClient(c *linear.Client) bool { return c != nil }

Prevention

When it happens

Trigger: Calling linear.BuildLabelCache(ctx, nil), typically because an enclosing struct's client field was never initialized or a factory returned nil without error propagation.

Common situations: Lazy client construction skipped when config load partially failed; a *Client variable left nil on an error path that returned nil, nil; tests constructing mapping helpers without a stub client.

Related errors


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