gastownhall/beads · error

Jira project not configured (set jira.project, jira.projects

Error message

Jira project not configured (set jira.project, jira.projects, or JIRA_PROJECT)

What it means

Tracker.Init resolves which Jira project keys the tracker operates on. It first honors pre-set keys (from CLI flags), then reads 'jira.projects' (plural) and 'jira.project' (singular) / JIRA_PROJECTS / JIRA_PROJECT through tracker.ResolveProjectIDs. If no keys result, Init fails with this message listing every accepted source.

Source

Thrown at internal/jira/tracker.go:77

func (t *Tracker) ConfigPrefix() string { return "jira" }

func (t *Tracker) Init(ctx context.Context, store storage.Storage) error {
	t.store = store

	jiraURL, err := t.getConfig(ctx, "jira.url", "JIRA_URL")
	if err != nil || jiraURL == "" {
		return fmt.Errorf("Jira URL not configured (set jira.url or JIRA_URL)")
	}
	t.jiraURL = jiraURL

	// Resolve project keys: use pre-set keys (from CLI), or fall back to config.
	if len(t.projectKeys) == 0 {
		pluralVal, _ := t.getConfig(ctx, "jira.projects", "JIRA_PROJECTS")
		singularVal, _ := t.getConfig(ctx, "jira.project", "JIRA_PROJECT")
		t.projectKeys = tracker.ResolveProjectIDs(nil, pluralVal, singularVal)
	}
	if len(t.projectKeys) == 0 {
		return fmt.Errorf("Jira project not configured (set jira.project, jira.projects, or JIRA_PROJECT)")
	}

	username, _ := t.getConfig(ctx, "jira.username", "JIRA_USERNAME")
	apiToken, err := t.getConfig(ctx, "jira.api_token", "JIRA_API_TOKEN")
	if err != nil || apiToken == "" {
		return fmt.Errorf("Jira API token not configured (set jira.api_token or JIRA_API_TOKEN)")
	}

	t.client = NewClient(jiraURL, username, apiToken)

	apiVersion, _ := t.getConfig(ctx, "jira.api_version", "JIRA_API_VERSION")
	if apiVersion == "" {
		apiVersion = "3"
	}
	t.apiVersion = apiVersion
	t.client.APIVersion = apiVersion

	// Load optional custom status map from all jira.status_map.* config keys.

View on GitHub (pinned to 71377f2769)

Solutions

  1. Set jira.projects or JIRA_PROJECTS with the project key(s) (e.g. JIRA_PROJECTS=BEADS,OPS).
  2. Or set the singular jira.project / JIRA_PROJECT.
  3. Or pass project keys via the CLI flags that populate t.projectKeys before Init.
  4. Confirm the keys are actual Jira project keys (uppercase short codes), not project names or issue prefixes.
  5. Check for empty-string env vars — an empty JIRA_PROJECTS can shadow a correct singular value depending on resolution order.

Example fix

// before
tracker := jira.NewTracker(); _ = tracker.Init(ctx, store)
// after
tracker := jira.NewTracker()
tracker.SetProjectKeys([]string{"BEADS"}) // or set JIRA_PROJECTS=BEADS
_ = tracker.Init(ctx, store)
Defensive patterns

Strategy: validation

Validate before calling

func hasProjectConfig() bool {
    return os.Getenv("JIRA_PROJECTS") != "" || os.Getenv("JIRA_PROJECT") != ""
}
// call before Init; or pass keys via tracker's CLI-provided SetProjectKeys

Try / catch

if err := tracker.Init(ctx, store); err != nil {
    if strings.Contains(err.Error(), "Jira project not configured") {
        return fmt.Errorf("set JIRA_PROJECTS=KEY1,KEY2 before starting")
    }
    return err
}

Prevention

When it happens

Trigger: Init is called with t.projectKeys empty AND all of jira.projects/JIRA_PROJECTS, jira.project/JIRA_PROJECT unset or empty after resolution.

Common situations: User set only the URL and token assuming a default project exists; project key set under a wrong config key; CLI flag not forwarded before Init; empty JIRA_PROJECTS exported as ''; multi-project config typo where ResolveProjectIDs receives blank strings.

Related errors


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