Skip to content

bug: 'Always Approve' for destructive slash commands silently fails when ruamel.yaml is missing from venv #27660

@pan-long

Description

@pan-long

Bug Description

The "Always Approve" button on destructive slash commands (/new, /reset, /undo, /clear) silently fails to persist. The user clicks "Always Approve," the gateway logs "User opted out of destructive slash confirm," but the approvals.destructive_slash_confirm: false setting never lands in config.yaml. On next /new, the confirmation prompt reappears. The user can click "Always Approve" dozens of times with zero effect.

Root Cause

save_config_value() in cli.py calls atomic_roundtrip_yaml_update() from utils.py, which imports ruamel.yaml. If ruamel.yaml is missing from the venv, the ImportError is caught and logged at ERROR level — but never surfaced to the user:

ERROR cli: Failed to save config: No module named 'ruamel'

The gateway code at gateway/run.py:12406-12420 catches exceptions from save_config_value and only logs a warning:

if choice == "always":
    try:
        from cli import save_config_value
        save_config_value("approvals.destructive_slash_confirm", False)
        logger.info("User opted out of destructive slash confirm ...")
    except Exception as exc:
        logger.warning("Failed to persist destructive_slash_confirm=false: %s", exc)

The info log fires before atomic_roundtrip_yaml_update actually executes (since it's called inside a try/except but the info log is outside the actual save), so the log says "opted out" regardless of whether the write succeeded. The user sees nothing wrong.

Why ruamel.yaml can be missing

ruamel.yaml IS declared in pyproject.toml as a required dependency. However, on installs that predate uv or installations done via pip install -e ., transitive dependencies may not be fully resolved. In this case the venv had 300+ packages but ruamel.yaml was absent.

Steps to Reproduce

  1. Ensure ruamel.yaml is NOT installed in the Hermes venv:
    /path/to/venv/bin/python -c "import ruamel.yaml"ModuleNotFoundError
  2. In a gateway session (Telegram), type /new
  3. Click Always Approve
  4. Check ~/.hermes/config.yamldestructive_slash_confirm is not present
  5. Check ~/.hermes/logs/errors.log — contains Failed to save config: No module named 'ruamel'
  6. Type /new again — confirmation prompt still appears

Actual Behavior

  • Button appears to work (no visible error)
  • Gateway log says "User opted out of destructive slash confirm" (misleading)
  • Config is never written
  • User is stuck in a loop — can click "Always Approve" forever with no effect

Expected Behavior

Either:

  1. The dependency on ruamel.yaml is enforced so it's never missing (preferred)
  2. OR the failure is surfaced to the user with a clear error message ("Failed to save preference — missing dependency: ruamel.yaml")

Additionally: the "opted out" log line should fire after the save succeeds, not before.

Environment

  • Hermes Agent: installed via pip install -e . in venv
  • OS: Debian Linux
  • Python: 3.11
  • Gateway: Telegram

Related

Suggested Fix

Option A (preferred): Guard the import or add ruamel.yaml as a hard runtime check at startup — refuse to start or warn loudly if it's absent, since config persistence is critical.

Option B: Surface the error to the user. The _maybe_confirm_destructive_slash handler could return an error message if save_config_value returns False, or the exception handler could include the actual error in the reply: "⚠️ Failed to save preference: No module named 'ruamel'. Try: pip install ruamel.yaml"

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Medium — degraded but workaround existsarea/configConfig system, migrations, profilescomp/cliCLI entry point, hermes_cli/, setup wizardcomp/gatewayGateway runner, session dispatch, deliverytype/bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions