{"record":{"id":"5d36423780842b5f","repo":"gastownhall/beads","slug":"jira-tracker-not-initialized","errorCode":null,"errorMessage":"Jira tracker not initialized","messagePattern":"Jira tracker not initialized","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/jira/tracker.go","lineNumber":175,"sourceCode":"\t\t\t\ttypeCustomFields[parts[0]][parts[1]] = parsed\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tcustomFields[suffix] = parsed\n\t\t}\n\t\tif len(customFields) > 0 {\n\t\t\tt.customFields = customFields\n\t\t}\n\t\tif len(typeCustomFields) > 0 {\n\t\t\tt.typeCustomFields = typeCustomFields\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (t *Tracker) Validate() error {\n\tif t.client == nil {\n\t\treturn fmt.Errorf(\"Jira tracker not initialized\")\n\t}\n\treturn nil\n}\n\nfunc (t *Tracker) Close() error { return nil }\n\nfunc (t *Tracker) FetchIssues(ctx context.Context, opts tracker.FetchOptions) ([]tracker.TrackerIssue, error) {\n\t// Build JQL query — use IN clause for multi-project.\n\tvar jql string\n\tif len(t.projectKeys) == 1 {\n\t\tjql = fmt.Sprintf(\"project = %q\", t.projectKeys[0])\n\t} else {\n\t\tquoted := make([]string, len(t.projectKeys))\n\t\tfor i, k := range t.projectKeys {\n\t\t\tquoted[i] = fmt.Sprintf(\"%q\", k)\n\t\t}\n\t\tjql = fmt.Sprintf(\"project IN (%s)\", strings.Join(quoted, \", \"))\n\t}","sourceCodeStart":157,"sourceCodeEnd":193,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/jira/tracker.go#L157-L193","documentation":"Tracker.Validate is a pre-flight sanity check that the Jira tracker has a usable HTTP client. The client is only set up during Init; if Validate is called on a Tracker constructed without Init (or whose construction failed), the nil client check fails and this error is returned so callers don't hit a nil-pointer panic later.","triggerScenarios":"Calling (&jira.Tracker{}).Validate(), calling Validate before Init, or calling Validate after Init returned an error and left the client unset.","commonSituations":"Wiring the tracker via dependency injection without invoking the constructor/Init; retry logic that ignores Init's error; tests constructing the struct literal directly; lifecycle ordering bugs where Validate runs before initialization.","solutions":["Call Init (or the constructor) and check its error before calling Validate","Ensure initialization ordering so Validate always runs after successful Init","If Init failed, fix the underlying config/credentials error instead of proceeding to Validate","In tests, use the real constructor rather than a zero-value Tracker literal"],"exampleFix":"// before\nt := &jira.Tracker{}\nif err := t.Validate(); err != nil { ... }\n// after\nt, err := jira.NewTracker(cfg) // or t.Init(ctx, cfg)\nif err != nil { return err }\nif err := t.Validate(); err != nil { return err }","handlingStrategy":"validation","validationCode":"if tracker == nil || tracker.Validate() != nil {\n\treturn fmt.Errorf(\"tracker not initialized; call Init before use\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always construct the tracker via its constructor/Init, never a zero-value struct literal","Run Validate as part of application startup after Init","Check Init's error before proceeding to Validate or any API calls","Encode the Init→Validate→Execute lifecycle in a factory function so ordering cannot be skipped"],"tags":["lifecycle","initialization","jira"],"backgroundTag":"component-not-initialized","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}