{"record":{"id":"7f730dd7c493f61f","repo":"gastownhall/beads","slug":"jira-api-token-not-configured-set-jira-api-token","errorCode":null,"errorMessage":"Jira API token not configured (set jira.api_token or JIRA_API_TOKEN)","messagePattern":"Jira API token not configured \\(set jira\\.api_token or JIRA_API_TOKEN\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/jira/tracker.go","lineNumber":83,"sourceCode":"\tif err != nil || jiraURL == \"\" {\n\t\treturn fmt.Errorf(\"Jira URL not configured (set jira.url or JIRA_URL)\")\n\t}\n\tt.jiraURL = jiraURL\n\n\t// Resolve project keys: use pre-set keys (from CLI), or fall back to config.\n\tif len(t.projectKeys) == 0 {\n\t\tpluralVal, _ := t.getConfig(ctx, \"jira.projects\", \"JIRA_PROJECTS\")\n\t\tsingularVal, _ := t.getConfig(ctx, \"jira.project\", \"JIRA_PROJECT\")\n\t\tt.projectKeys = tracker.ResolveProjectIDs(nil, pluralVal, singularVal)\n\t}\n\tif len(t.projectKeys) == 0 {\n\t\treturn fmt.Errorf(\"Jira project not configured (set jira.project, jira.projects, or JIRA_PROJECT)\")\n\t}\n\n\tusername, _ := t.getConfig(ctx, \"jira.username\", \"JIRA_USERNAME\")\n\tapiToken, err := t.getConfig(ctx, \"jira.api_token\", \"JIRA_API_TOKEN\")\n\tif err != nil || apiToken == \"\" {\n\t\treturn fmt.Errorf(\"Jira API token not configured (set jira.api_token or JIRA_API_TOKEN)\")\n\t}\n\n\tt.client = NewClient(jiraURL, username, apiToken)\n\n\tapiVersion, _ := t.getConfig(ctx, \"jira.api_version\", \"JIRA_API_VERSION\")\n\tif apiVersion == \"\" {\n\t\tapiVersion = \"3\"\n\t}\n\tt.apiVersion = apiVersion\n\tt.client.APIVersion = apiVersion\n\n\t// Load optional custom status map from all jira.status_map.* config keys.\n\t// Using GetAllConfig supports arbitrary (including custom) beads status names.\n\tif allConfig, err := t.store.GetAllConfig(ctx); err == nil {\n\t\tconst statusPrefix = \"jira.status_map.\"\n\t\tstatusMap := make(map[string]string)\n\t\tfor key, val := range allConfig {\n\t\t\tif strings.HasPrefix(key, statusPrefix) && val != \"\" {","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/jira/tracker.go#L65-L101","documentation":"Tracker.Init requires an API token to authenticate the Jira client. It reads 'jira.api_token' with JIRA_API_TOKEN as env fallback; if the lookup errors or the token is empty, Init aborts with this message. A username is read alongside it (optional for some auth modes) but the token itself is mandatory — the client cannot sign requests without it.","triggerScenarios":"Init reaches the credential check (URL and project already valid) and jira.api_token/JIRA_API_TOKEN is missing, empty, or getConfig errors.","commonSituations":"Token stored in a secret manager not injected into the environment; token rotated/expired and removed; running locally where .env is not loaded; mismatched variable name (ATLASSIAN_API_TOKEN, JIRA_TOKEN); username set but token forgotten.","solutions":["Generate an API token at id.atlassian.com (Cloud) or set a PAT (Server/DC) and export JIRA_API_TOKEN with it.","Or set jira.api_token in the config store.","Verify the secret is actually injected into the runtime environment (CI secrets, k8s secret mounts, .env loading).","Confirm the variable name matches JIRA_API_TOKEN exactly.","Pair it with the correct username/email — a token without its matching account will fail later with 401 even when init passes."],"exampleFix":"// before: token not in environment\n_ = tracker.Init(ctx, store) // → error\n// after\nos.Setenv(\"JIRA_API_TOKEN\", os.Getenv(\"ATLASSIAN_API_TOKEN\"))\nos.Setenv(\"JIRA_USERNAME\", \"dev@yourorg.com\")\n_ = tracker.Init(ctx, store)","handlingStrategy":"validation","validationCode":"func hasTokenConfig() bool {\n    return os.Getenv(\"JIRA_API_TOKEN\") != \"\"\n}\n// call before Init; also verify JIRA_USERNAME is set for Cloud basic auth","typeGuard":null,"tryCatchPattern":"if err := tracker.Init(ctx, store); err != nil {\n    if strings.Contains(err.Error(), \"API token not configured\") {\n        return fmt.Errorf(\"export JIRA_API_TOKEN (generate at id.atlassian.com)\")\n    }\n    return err\n}","preventionTips":["Store the token in a secret manager and inject it as JIRA_API_TOKEN at runtime.","Rotate tokens before expiry and update all environments.","Verify secret injection in CI/containers (print var presence, never the value).","Set the matching JIRA_USERNAME/email so auth succeeds after init.","Use exact variable name JIRA_API_TOKEN; avoid alias names that the tracker ignores."],"tags":["jira","configuration","authentication","api-token","environment"],"backgroundTag":"missing-env-var","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}