Panniantong/Agent-Reach · error · GitHubConfigError

gh hosts.yml 无法安全读取

Error message

gh hosts.yml 无法安全读取

What it means

Raised by _saved_github_host_configured() when the gh CLI's ~/.config/gh/hosts.yml cannot be safely read. Reading goes through read_small_text_no_follow(), which refuses symlinked path components, non-regular files, files over _MAX_HOSTS_BYTES, and raises OSError/UnicodeError for other I/O or UTF-8 decode failures. Any of these makes the GitHub channel's credential probe fail closed with GitHubConfigError.

Source

Thrown at agent_reach/channels/github.py:60

    if os.name == "nt":
        app_data = os.environ.get("APPDATA")
        if app_data:
            return Path(app_data) / "GitHub CLI" / "hosts.yml"

    return Path.home() / ".config" / "gh" / "hosts.yml"


def _saved_github_host_configured() -> bool:
    """Inspect github.com's hosts.yml entry without executing gh."""
    hosts_path = _gh_hosts_path()
    try:
        raw = read_small_text_no_follow(
            hosts_path,
            max_bytes=_MAX_HOSTS_BYTES,
        )
    except (OSError, PrivatePathError, UnicodeError) as exc:
        raise GitHubConfigError("gh hosts.yml 无法安全读取") from exc
    if raw is None:
        return False
    try:
        payload = yaml.safe_load(raw)
    except yaml.YAMLError as exc:
        raise GitHubConfigError("gh hosts.yml 不是有效的 UTF-8 YAML") from exc
    if payload is None:
        return False
    if not isinstance(payload, dict):
        raise GitHubConfigError("gh hosts.yml 顶层必须是对象")

    host = payload.get("github.com")
    if host is None:
        return False
    if not isinstance(host, dict):
        raise GitHubConfigError("gh hosts.yml 的 github.com 配置无效")

    users = host.get("users")

View on GitHub (pinned to 93ae1d18c3)

Solutions

  1. Inspect ~/.config/gh/hosts.yml: ensure it is a real file with no symlinks in its path (ls -la ~/.config/gh/) and replace symlinks with real files or bind mounts
  2. Check permissions: chmod 600 ~/.config/gh/hosts.yml and confirm you own it
  3. If the file is corrupt, run `gh auth login` again to regenerate a clean hosts.yml
  4. If you auth via token instead, export GH_TOKEN or GITHUB_TOKEN — _explicit_github_credentials() is checked before this path is relied on
  5. As a last resort remove hosts.yml; read returns None and the check reports 'not configured' instead of raising

Example fix

# before: ~/.config/gh/hosts.yml is a symlink to ~/dotfiles/gh-hosts.yml
# after: real file
 cp ~/dotfiles/gh-hosts.yml ~/.config/gh/hosts.yml && chmod 600 ~/.config/gh/hosts.yml
Defensive patterns

Strategy: try-catch

Validate before calling

from pathlib import Path
import os

def hosts_readable() -> bool:
    p = Path.home() / ".config" / "gh" / "hosts.yml"
    if not p.exists() or p.is_symlink() or p.parent.is_symlink():
        return False
    try:
        st = p.stat()
        return st.st_size <= 64 * 1024 and os.access(p, os.R_OK)
    except OSError:
        return False

Try / catch

from agent_reach.channels.github import GitHubConfigError
try:
    configured = _saved_github_host_configured()
except GitHubConfigError as exc:
    # degrade to 'not configured', surface hint to re-run gh auth login
    report_config_hint(str(exc))
else:
    use(configured)

Prevention

When it happens

Trigger: Calling GitHubChannel.check()/doctor when: hosts.yml is a symlink or sits on a symlinked directory; the file is larger than _MAX_HOSTS_BYTES; the file is a FIFO/device; permissions deny read (OSError/EPERM); or the file contains bytes that are not valid UTF-8 (UnicodeError).

Common situations: Users manage dotfiles via symlinks (stow, chezmoi) so ~/.config/gh or hosts.yml is a link; hosts.yml got truncated/corrupted; disk-full or permission changes after a system migration; a non-UTF-8 editor wrote the file.

Related errors


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