Panniantong/Agent-Reach · error · ConfigReadOnlyError
当前配置是只读的,不能保存
Error message
当前配置是只读的,不能保存
What it means
Config supports an explicit read-only mode (Config(read_only=True); the CLI uses it for --dry-run and safe mode). Calling save() on such an instance raises ConfigReadOnlyError('当前配置是只读的,不能保存') ('the current config is read-only and cannot be saved') before touching the filesystem. This is a deliberate guard so inspection flows can never mutate credentials.
Source
Thrown at agent_reach/config.py:155
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
return default
def set(self, key: str, value: Any):
"""Set a config value and save."""
if self.read_only:
raise ConfigReadOnlyError("当前配置是只读的,不能修改")View on GitHub (pinned to 93ae1d18c3)
Solutions
- Create a writable instance when you intend to persist: config = Config() (read_only defaults to False)
- Check the mode before saving: if config.read_only: re-open writable or skip
- In CLI flows, never route mutations through the dry-run/safe-mode Config instance
- Catch ConfigReadOnlyError specifically (it subclasses ConfigError) to report 'skipped write in read-only mode' instead of crashing
Example fix
# before
cfg = Config(read_only=True) # e.g. reused from a dry-run path
cfg.set('github_token', tok)
cfg.save() # raises ConfigReadOnlyError
# after
cfg = Config() # writable when you need to save
cfg.set('github_token', tok)
cfg.save() Defensive patterns
Strategy: type-guard
Validate before calling
from agent_reach.config import Config
cfg = Config()
if cfg.read_only:
cfg = Config() # re-open writable before planning any save() Type guard
from agent_reach.config import Config
def is_writable_config(cfg: Config) -> bool:
"""Type/mode guard: True only when save() is permitted."""
return isinstance(cfg, Config) and not getattr(cfg, "read_only", False) Try / catch
from agent_reach.config import ConfigReadOnlyError
try:
cfg.set(key, value)
cfg.save()
except ConfigReadOnlyError:
log.info("skipped persisting %s: config opened read-only", key) Prevention
- Never share one Config instance between inspection (read_only=True) and mutation flows
- Check `cfg.read_only` before calling set/save in library code that receives a Config
- Reserve read-only mode for doctor/dry-run; create a fresh writable Config for configure paths
When it happens
Trigger: Library code that holds a Config(read_only=True) instance (e.g. during doctor/dry-run) and then calls set(...) + save(); mixing a read-only Config into a code path that assumes write access; writing a helper that receives a Config without knowing its mode.
Common situations: Integrations reusing the CLI's read-only Config object for convenience; new contributors adding write features without checking the flag; dry-run code paths accidentally reaching a save() call.
Related errors
- 当前配置是只读的,不能修改
- gh hosts.yml 无法安全读取
- gh hosts.yml 不是有效的 UTF-8 YAML
- gh hosts.yml 顶层必须是对象
- gh hosts.yml 的 github.com 配置无效
AI-assisted analysis of Panniantong/Agent-Reach@93ae1d18c3 (2026-08-14).
Data as JSON: /api/errors/29c8ac012493a42f.
Report an issue: GitHub.