gastownhall/beads · error

repos section is not a map

Error message

repos section is not a map

What it means

GetReposFromYAML expects the top-level `repos` key in config.yaml to be a mapping (with optional `primary` and `additional` keys). When `repos:` parses to something else — a string, number, or list — the type assertion to map[string]interface{} fails and this error is returned.

Source

Thrown at internal/config/repos.go:54

	data, err := os.ReadFile(configPath) // #nosec G304 - config file path from caller
	if err != nil {
		if os.IsNotExist(err) {
			return &ReposConfig{}, nil
		}
		return nil, fmt.Errorf("failed to read config.yaml: %w", err)
	}

	// Parse into a generic map to extract repos section
	var cfg map[string]interface{}
	if err := yaml.Unmarshal(data, &cfg); err != nil {
		return nil, fmt.Errorf("failed to parse config.yaml: %w", err)
	}

	repos := &ReposConfig{}
	if reposRaw, ok := cfg["repos"]; ok && reposRaw != nil {
		reposMap, ok := reposRaw.(map[string]interface{})
		if !ok {
			return nil, fmt.Errorf("repos section is not a map")
		}

		if primary, ok := reposMap["primary"].(string); ok {
			repos.Primary = primary
		}

		if additional, ok := reposMap["additional"]; ok && additional != nil {
			switch v := additional.(type) {
			case []interface{}:
				for _, item := range v {
					if str, ok := item.(string); ok {
						repos.Additional = append(repos.Additional, str)
					}
				}
			}
		}
	}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Restructure the repos section as a mapping with primary/additional keys.
  2. Example correct shape: repos:\n primary: "."\n additional: ["../other-repo"].
  3. Re-run the command after fixing; or delete the repos key and let `bd repo add` regenerate it.

Example fix

// before
repos: ../other-repo
// after
repos:
  primary: "."
  additional:
    - "../other-repo"
Defensive patterns

Strategy: type-guard

Validate before calling

var cfg map[string]interface{}
if data, err := os.ReadFile(configPath); err == nil {
    if yaml.Unmarshal(data, &cfg) == nil {
        if raw, ok := cfg["repos"]; ok && raw != nil {
            if _, ok := raw.(map[string]interface{}); !ok {
                return fmt.Errorf("repos must be a mapping with primary/additional keys")
            }
        }
    }
}

Type guard

func reposIsMap(cfg map[string]interface{}) bool {
    raw, ok := cfg["repos"]
    if !ok || raw == nil { return true }
    _, ok = raw.(map[string]interface{})
    return ok
}

Try / catch

repos, err := config.GetReposFromYAML(configPath)
if err != nil && strings.Contains(err.Error(), "repos section is not a map") {
    return fmt.Errorf("fix `repos:` in %s to a mapping: repos:\\n  primary: ...", configPath)
}

Prevention

When it happens

Trigger: Calling GetReposFromYAML (via AddRepo/RemoveRepo/ListRepos) with a config.yaml containing e.g. `repos: foo`, `repos: [a, b]`, or `repos: 3` instead of `repos:\n primary: ...\n additional: [...]`.

Common situations: Hand-editing the config and writing the repo path directly under `repos:` instead of nesting it; misreading the schema and using a list of repos; a malformed merge that flattened the section.

Related errors


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