Panniantong/Agent-Reach · error · ConfigError

配置文件顶层必须是对象

Error message

配置文件顶层必须是对象

What it means

Config.load() parses ~/.agent-reach/config.yaml with yaml.safe_load and requires the top-level node to be a mapping. If the file's top level is a list, scalar, or string, ConfigError('配置文件顶层必须是对象') ('the config file top level must be an object') is raised on every Config construction that loads.

Source

Thrown at agent_reach/config.py:149

    def load(self):
        """Load config from YAML file."""
        _reject_symlink(self.config_dir, "配置目录")
        _reject_symlink(self.config_path, "配置文件")
        try:
            payload = read_small_text_no_follow(
                self.config_path,
                max_bytes=_MAX_CONFIG_BYTES,
            )
        except PrivatePathError as exc:
            raise ConfigSecurityError(str(exc)) from exc
        if payload is None:
            self.data = {}
            return

        loaded = yaml.safe_load(payload) or {}
        if not isinstance(loaded, dict):
            raise ConfigError("配置文件顶层必须是对象")
        self.data = loaded

    def save(self):
        """Save config atomically, refusing mutation in read-only mode."""
        if self.read_only:
            raise ConfigReadOnlyError("当前配置是只读的,不能保存")
        self._ensure_dir()
        _atomic_write_yaml(self.config_path, self.data)

    def get(self, key: str, default: Any = None) -> Any:
        """Get a config value. Also checks environment variables (uppercase)."""
        # Config file first
        if key in self.data:
            return self.data[key]
        # Then env var (uppercase)
        env_val = os.environ.get(key.upper())
        if env_val:
            return env_val

View on GitHub (pinned to 93ae1d18c3)

Solutions

  1. Open ~/.agent-reach/config.yaml and ensure the first non-comment line is a key like 'twitter_cookies: ...', not a '- ' list item or plain text
  2. Validate locally: python -c "import yaml,sys; d=yaml.safe_load(open('/home/you/.agent-reach/config.yaml')); print(type(d))" — must print <class 'dict'>
  3. If unsure, back up and recreate: mv config.yaml config.yaml.bak, then run `agent-reach configure` for each key you need
  4. Never write the config by hand redirection; use `agent-reach configure <key>` which writes valid YAML atomically

Example fix

# before (~/.agent-reach/config.yaml root is a list)
- github_token: ghp_xxx
- proxy: http://127.0.0.1:7890

# after (root is a mapping)
github_token: ghp_xxx
proxy: http://127.0.0.1:7890
Defensive patterns

Strategy: try-catch

Validate before calling

import yaml
from pathlib import Path

def config_is_valid_mapping(path: Path) -> bool:
    if not path.exists():
        return True  # absent file is fine; Config treats it as empty
    loaded = yaml.safe_load(path.read_text(encoding="utf-8"))
    return loaded is None or isinstance(loaded, dict)

Try / catch

from agent_reach.config import Config, ConfigError

try:
    cfg = Config()
    cfg.load()
except ConfigError as exc:
    if "顶层必须是对象" in str(exc):
        quarantine_and_recreate_config()  # mv bad file aside, reconfigure keys
    else:
        raise

Prevention

When it happens

Trigger: A config.yaml whose root is '- key: value' (list), a bare string, or an empty-but-quoted document after hand-editing; files created by redirecting output (`echo something > ~/.agent-reach/config.yaml`); YAML with a document separator producing a non-map root.

Common situations: Hand-editing the config and accidentally wrapping everything in a list; scripts generating YAML with a top-level sequence; partial writes from a crash leaving a truncated non-map document; copy-pasting a YAML fragment that starts with '-'.

Related errors


AI-assisted analysis of Panniantong/Agent-Reach@93ae1d18c3 (2026-08-14). Data as JSON: /api/errors/c5ef1388bf5e6496. Report an issue: GitHub.