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

  1. Create a writable instance when you intend to persist: config = Config() (read_only defaults to False)
  2. Check the mode before saving: if config.read_only: re-open writable or skip
  3. In CLI flows, never route mutations through the dry-run/safe-mode Config instance
  4. 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

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


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