gastownhall/beads · error

beadsDir must not be empty

Error message

beadsDir must not be empty

What it means

WriteLastPullTimestamp records the current UTC time (ISO 8601) into <beadsDir>/.beads/last_pull to support pull staleness checks. It validates its input first: if beadsDir is an empty string it returns this sentinel error instead of attempting to write a file at a meaningless path. This is a programmer-error guard, not a runtime condition.

Source

Thrown at internal/linear/staleness.go:20

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"time"
)

const (
	lastPullFileName      = "last_pull"
	DefaultStaleThreshold = 20 * time.Minute
	debounceThreshold     = 5 * time.Minute
)

// WriteLastPullTimestamp writes the current time as ISO 8601 to .beads/last_pull.
func WriteLastPullTimestamp(beadsDir string) error {
	if beadsDir == "" {
		return fmt.Errorf("beadsDir must not be empty")
	}
	path := filepath.Join(beadsDir, lastPullFileName)
	ts := time.Now().UTC().Format(time.RFC3339)
	return os.WriteFile(path, []byte(ts+"\n"), 0600)
}

// ReadLastPullTimestamp reads the last pull timestamp from .beads/last_pull.
// Returns the zero time if the file doesn't exist or is unreadable.
func ReadLastPullTimestamp(beadsDir string) (time.Time, error) {
	if beadsDir == "" {
		return time.Time{}, fmt.Errorf("beadsDir must not be empty")
	}
	path := filepath.Join(beadsDir, lastPullFileName)
	data, err := os.ReadFile(path) // #nosec G304 -- path is constrained to the beads directory.
	if err != nil {
		if os.IsNotExist(err) {
			return time.Time{}, nil
		}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Ensure the beads directory is resolved before calling (default to ./.beads or the project root's .beads)
  2. Check the flag/env/config value feeding beadsDir for emptiness at startup
  3. Return or surface the configuration error earlier instead of passing an empty string down
  4. In tests, pass t.TempDir() rather than ""

Example fix

// before
WriteLastPullTimestamp(cfg.BeadsDir) // cfg.BeadsDir may be ""
// after
if cfg.BeadsDir == "" {
    return fmt.Errorf("beads dir not configured")
}
WriteLastPullTimestamp(cfg.BeadsDir)
Defensive patterns

Strategy: validation

Validate before calling

if beadsDir == "" {
    return fmt.Errorf("beads dir is not configured")
}
// safe to call
err := WriteLastPullTimestamp(beadsDir)

Type guard

null

Try / catch

null

Prevention

When it happens

Trigger: Calling WriteLastPullTimestamp("") — typically because the caller derived the beads directory from an unset config value, empty flag, or failed path resolution before writing the timestamp after a pull.

Common situations: A --beads-dir flag left empty and not defaulted; an environment variable (e.g. BEADS_DIR) unset; a config struct field not populated because initialization was skipped in tests or a new code path.

Related errors


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