{"record":{"id":"49d327e25720a6de","repo":"Panniantong/Agent-Reach","slug":"gh-hosts-yml-49d327","errorCode":null,"errorMessage":"gh hosts.yml 顶层必须是对象","messagePattern":"gh hosts\\.yml 顶层必须是对象","errorType":"exception","errorClass":"GitHubConfigError","httpStatus":null,"severity":"error","filePath":"agent_reach/channels/github.py","lineNumber":70,"sourceCode":"    \"\"\"Inspect github.com's hosts.yml entry without executing gh.\"\"\"\n    hosts_path = _gh_hosts_path()\n    try:\n        raw = read_small_text_no_follow(\n            hosts_path,\n            max_bytes=_MAX_HOSTS_BYTES,\n        )\n    except (OSError, PrivatePathError, UnicodeError) as exc:\n        raise GitHubConfigError(\"gh hosts.yml 无法安全读取\") from exc\n    if raw is None:\n        return False\n    try:\n        payload = yaml.safe_load(raw)\n    except yaml.YAMLError as exc:\n        raise GitHubConfigError(\"gh hosts.yml 不是有效的 UTF-8 YAML\") from exc\n    if payload is None:\n        return False\n    if not isinstance(payload, dict):\n        raise GitHubConfigError(\"gh hosts.yml 顶层必须是对象\")\n\n    host = payload.get(\"github.com\")\n    if host is None:\n        return False\n    if not isinstance(host, dict):\n        raise GitHubConfigError(\"gh hosts.yml 的 github.com 配置无效\")\n\n    users = host.get(\"users\")\n    if users is not None and not isinstance(users, dict):\n        raise GitHubConfigError(\"gh hosts.yml 的 users 配置无效\")\n    return bool(host.get(\"oauth_token\") or host.get(\"user\") or users)\n\n\ndef _explicit_github_credentials(config) -> bool:\n    if any(os.environ.get(name) for name in (\"GH_TOKEN\", \"GITHUB_TOKEN\")):\n        return True\n    if config is None:\n        return False","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/Panniantong/Agent-Reach/blob/93ae1d18c37b707dec053c7c4f9d91cd8ef8943d/agent_reach/channels/github.py#L52-L88","documentation":"Raised when yaml.safe_load() succeeds but the top-level YAML value is not a mapping — e.g. the file contains a plain scalar, a list, or a multi-document stream whose first node is not a dict. gh's hosts.yml must be a map keyed by hostname.","triggerScenarios":"hosts.yml contains just a token string ('gho_xxx'), a YAML list ('- github.com'), or '---\\njust some text'. safe_load returns str/list, isinstance(payload, dict) fails, error raised. Note: empty file → payload is None → returns False (no error).","commonSituations":"Users echo a raw token into hosts.yml instead of using gh auth login; a template placeholder like '${TOKEN}' left unreplaced yields a scalar; accidental shell redirect truncation to a stray word.","solutions":["Rewrite hosts.yml to the correct shape: a top-level mapping with hostname keys (github.com: {oauth_token: ..., user: ...})","Prefer `gh auth login` over hand-editing","If YAML anchors/double documents are used, flatten to a single mapping document"],"exampleFix":"# before\n gho_abc123...\n# after\n github.com:\n   oauth_token: gho_abc123...\n   user: myname","handlingStrategy":"validation","validationCode":"import yaml\n\ndef hosts_toplevel_is_map(path) -> bool:\n    try:\n        payload = yaml.safe_load(open(path, encoding=\"utf-8\").read())\n    except Exception:\n        return False\n    return payload is None or isinstance(payload, dict)","typeGuard":null,"tryCatchPattern":"try:\n    _saved_github_host_configured()\nexcept GitHubConfigError:\n    treat_github_as_unconfigured()","preventionTips":["hosts.yml top level must be hostname-keyed mappings only","Never `echo token > hosts.yml`; use gh auth login","After templating config, assert the rendered doc is a dict"],"tags":["github","gh-cli","yaml","config"],"backgroundTag":null,"analyzedSha":"93ae1d18c37b707dec053c7c4f9d91cd8ef8943d","analyzedAt":"2026-08-14T22:54:06.735Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}