gastownhall/beads · error

migrating config to metadata.json: %w

Error message

migrating config to metadata.json: %w

What it means

During the legacy config.json -> metadata.json migration inside Load(), after successfully parsing the legacy file, bd writes the migrated config with Config.Save. This error wraps any failure of that save (temp-file creation, write, chmod, rename inside the beads directory). The migration aborts and the legacy file is left in place, so the next Load retries the migration.

Source

Thrown at internal/configfile/configfile.go:105

		// Try legacy config.json location (migration path)
		legacyPath := filepath.Join(beadsDir, "config.json")
		data, err = os.ReadFile(legacyPath) // #nosec G304 - controlled path from config
		if os.IsNotExist(err) {
			return nil, nil
		}
		if err != nil {
			return nil, fmt.Errorf("reading legacy config: %w", err)
		}

		// Migrate: parse legacy config, save as metadata.json, remove old file
		var cfg Config
		if err := json.Unmarshal(data, &cfg); err != nil {
			return nil, fmt.Errorf("parsing legacy config: %w", err)
		}

		// Save to new location
		if err := cfg.Save(beadsDir); err != nil {
			return nil, fmt.Errorf("migrating config to metadata.json: %w", err)
		}

		// Remove legacy file (best effort: migration already saved to new location)
		_ = os.Remove(legacyPath)

		return &cfg, nil
	}
	if err != nil {
		return nil, fmt.Errorf("reading config: %w", err)
	}

	var cfg Config
	if err := json.Unmarshal(data, &cfg); err != nil {
		return nil, fmt.Errorf("parsing config: %w", err)
	}

	return &cfg, nil
}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Check write permissions on the .beads directory (ls -ld .beads) and fix with chown/chmod
  2. Free disk space if the filesystem is full
  3. Remove stale metadata.json.tmp-* files, then rerun bd
  4. If the directory is intentionally read-only, migrate the config on a writable machine or run bd once with elevated access

Example fix

// before
$ bd ready
Error: parsing... migrating config to metadata.json: permission denied
// after
$ chmod u+w .beads && bd ready
Defensive patterns

Strategy: try-catch

Validate before calling

info, err := os.Stat(beadsDir)
if err != nil || !info.IsDir() {
	return fmt.Errorf("beads dir missing")
}
if f, err := os.OpenFile(filepath.Join(beadsDir, ".write-probe"), os.O_CREATE|os.O_WRONLY, 0o600); err != nil {
	return fmt.Errorf("beads dir not writable: %w", err)
} else {
	f.Close(); os.Remove(filepath.Join(beadsDir, ".write-probe"))
}

Try / catch

cfg, err := configfile.Load(beadsDir)
if err != nil && strings.Contains(err.Error(), "migrating config to metadata.json") {
	// check disk space and .beads permissions, then retry Load
	err = retryLoad(beadsDir)
}

Prevention

When it happens

Trigger: Calling configfile.Load(beadsDir) when metadata.json is missing, config.json parses fine, but Save fails because the beads dir is read-only, disk is full, permissions deny writing metadata.json, or .beads was deleted between read and save.

Common situations: Read-only checkouts or mounted volumes; .beads owned by another user; disk quota exceeded; a stale metadata.json.tmp-* left by a crashed process (harmless, but a full filesystem is not).

Related errors


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