{"record":{"id":"c5ef1388bf5e6496","repo":"Panniantong/Agent-Reach","slug":"error","errorCode":null,"errorMessage":"配置文件顶层必须是对象","messagePattern":"配置文件顶层必须是对象","errorType":"validation","errorClass":"ConfigError","httpStatus":null,"severity":"error","filePath":"agent_reach/config.py","lineNumber":149,"sourceCode":"\n    def load(self):\n        \"\"\"Load config from YAML file.\"\"\"\n        _reject_symlink(self.config_dir, \"配置目录\")\n        _reject_symlink(self.config_path, \"配置文件\")\n        try:\n            payload = read_small_text_no_follow(\n                self.config_path,\n                max_bytes=_MAX_CONFIG_BYTES,\n            )\n        except PrivatePathError as exc:\n            raise ConfigSecurityError(str(exc)) from exc\n        if payload is None:\n            self.data = {}\n            return\n\n        loaded = yaml.safe_load(payload) or {}\n        if not isinstance(loaded, dict):\n            raise ConfigError(\"配置文件顶层必须是对象\")\n        self.data = loaded\n\n    def save(self):\n        \"\"\"Save config atomically, refusing mutation in read-only mode.\"\"\"\n        if self.read_only:\n            raise ConfigReadOnlyError(\"当前配置是只读的，不能保存\")\n        self._ensure_dir()\n        _atomic_write_yaml(self.config_path, self.data)\n\n    def get(self, key: str, default: Any = None) -> Any:\n        \"\"\"Get a config value. Also checks environment variables (uppercase).\"\"\"\n        # Config file first\n        if key in self.data:\n            return self.data[key]\n        # Then env var (uppercase)\n        env_val = os.environ.get(key.upper())\n        if env_val:\n            return env_val","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/Panniantong/Agent-Reach/blob/93ae1d18c37b707dec053c7c4f9d91cd8ef8943d/agent_reach/config.py#L131-L167","documentation":"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.","triggerScenarios":"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.","commonSituations":"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 '-'.","solutions":["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","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'>","If unsure, back up and recreate: mv config.yaml config.yaml.bak, then run `agent-reach configure` for each key you need","Never write the config by hand redirection; use `agent-reach configure <key>` which writes valid YAML atomically"],"exampleFix":"# before (~/.agent-reach/config.yaml root is a list)\n- github_token: ghp_xxx\n- proxy: http://127.0.0.1:7890\n\n# after (root is a mapping)\ngithub_token: ghp_xxx\nproxy: http://127.0.0.1:7890","handlingStrategy":"try-catch","validationCode":"import yaml\nfrom pathlib import Path\n\ndef config_is_valid_mapping(path: Path) -> bool:\n    if not path.exists():\n        return True  # absent file is fine; Config treats it as empty\n    loaded = yaml.safe_load(path.read_text(encoding=\"utf-8\"))\n    return loaded is None or isinstance(loaded, dict)","typeGuard":null,"tryCatchPattern":"from agent_reach.config import Config, ConfigError\n\ntry:\n    cfg = Config()\n    cfg.load()\nexcept ConfigError as exc:\n    if \"顶层必须是对象\" in str(exc):\n        quarantine_and_recreate_config()  # mv bad file aside, reconfigure keys\n    else:\n        raise","preventionTips":["Only mutate the config through `agent-reach configure <key>`, which writes valid YAML atomically","After any hand edit, validate with yaml.safe_load and assert the result is a dict","Keep the root as key: value pairs — never wrap the document in a list"],"tags":["config","yaml","parsing","invalid-format"],"backgroundTag":null,"analyzedSha":"93ae1d18c37b707dec053c7c4f9d91cd8ef8943d","analyzedAt":"2026-08-14T22:54:06.735Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}