{"record":{"id":"72f4ea072e8a2f5d","repo":"gastownhall/beads","slug":"no-linear-client-available","errorCode":null,"errorMessage":"no Linear client available","messagePattern":"no Linear client available","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/linear/parent_reconcile.go","lineNumber":26,"sourceCode":"\n// fetchIssueAcrossTeams locates an issue by its Linear identifier across\n// all configured team clients. Single-team setups hit the primary client\n// directly; multi-team setups fall through each client in order until one\n// returns a non-nil issue. Returns (issue, hostClient, nil) on success;\n// the hostClient is the team's client that owned the issue and should be\n// reused for subsequent mutations (so the update path doesn't re-probe\n// and silently fall back to the wrong client on transient probe errors —\n// see clientForExternalID's fallback behavior in tracker.go).\n//\n// Returns (nil, nil, nil) when no team has the issue.\nfunc (t *Tracker) fetchIssueAcrossTeams(ctx context.Context, identifier string) (*Issue, *Client, error) {\n\tif identifier == \"\" {\n\t\treturn nil, nil, nil\n\t}\n\tif len(t.teamIDs) <= 1 {\n\t\tclient := t.primaryClient()\n\t\tif client == nil {\n\t\t\treturn nil, nil, errors.New(\"no Linear client available\")\n\t\t}\n\t\tli, err := client.FetchIssueByIdentifier(ctx, identifier)\n\t\tif err != nil {\n\t\t\treturn nil, nil, err\n\t\t}\n\t\tif li == nil {\n\t\t\treturn nil, nil, nil\n\t\t}\n\t\treturn li, client, nil\n\t}\n\t// Multi-team: try each client. First non-nil result wins. Rate-limit\n\t// errors abort immediately (the cross-team probe shouldn't burn\n\t// through quota when the circuit breaker has already tripped).\n\tfor _, teamID := range t.teamIDs {\n\t\tclient := t.clients[teamID]\n\t\tif client == nil {\n\t\t\tcontinue\n\t\t}","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/linear/parent_reconcile.go#L8-L44","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\ntracker := &linear.Tracker{}\nstats, err := tracker.ReconcileParents(ctx, links, false)\n// after\nif tracker.PrimaryClient() == nil {\n    return errors.New(\"linear: cannot reconcile parents without a client; set the API key\")\n}\nstats, err := tracker.ReconcileParents(ctx, links, false)","handlingStrategy":"type-guard","validationCode":"if tracker.PrimaryClient() == nil {\n    return errors.New(\"linear client not configured; set the Linear API key\")\n}","typeGuard":"func (t *Tracker) HasClient() bool { return t.PrimaryClient() != nil }","tryCatchPattern":"li, _, err := fetchIssueAcrossTeams(ctx, tracker, identifier)\nif err != nil && err.Error() == \"no Linear client available\" {\n    return nil, fmt.Errorf(\"linear: configure an API client before fetching %s\", identifier)\n}","preventionTips":["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"],"tags":["linear","integration","configuration","nil-client"],"backgroundTag":"missing-api-client","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}