hermes - ✅(Solved) Fix voice.record_key: empty string crashes REPL startup with 'Invalid key' [2 pull requests, 3 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
NousResearch/hermes-agent#19915Fetched 2026-05-05 06:04:22
View on GitHub
Comments
3
Participants
2
Timeline
10
Reactions
0
Author
Participants
Timeline (top)
labeled ×4commented ×3cross-referenced ×3

Error Message

Error: Invalid key:

Root Cause

In cli.py, three locations read voice.record_key via load_config().get("voice", {}).get("record_key", "ctrl+b"):

  • Line ~8579: voice mode activation display
  • Line ~8649: voice mode status display
  • Line ~10564: REPL keybinding registration (@kb.add(_voice_key))

The .get("record_key", "ctrl+b") default only applies when the key is absent from the config dict. When record_key: "" is explicitly set (e.g., intentionally for hands-free VAD mode per docs), the empty string is used directly, causing the crash at line ~10564.

Fix Action

Fixed

PR fix notes

PR #19929: Fix empty voice.record_key in CLI VAD mode

Description (problem / solution / changelog)

Closes #19915

Summary:

  • treat voice.record_key: "" as intentional hands-free VAD mode instead of trying to register an empty prompt_toolkit keybinding
  • centralize voice record-key normalization/display so voice enable/status output stays consistent
  • add regression tests that prove empty config skips keybinding registration

Validation:

  • python -m pytest tests/cli/test_voice_record_key.py -o 'addopts=' -q\n

Changed files

  • cli.py (modified, +33/-15)
  • tests/cli/test_voice_record_key.py (added, +77/-0)

PR #19945: fix: harden voice key and custom provider parsing

Description (problem / solution / changelog)

Summary

This PR fixes two config/model-selection regressions reported in upstream issues:

  1. voice.record_key: "" no longer crashes CLI startup.

    • Empty, whitespace-only, or non-string values now normalize back to the safe default ctrl+b.
    • The same normalization path is reused for startup messaging, /voice status output, and prompt_toolkit keybinding registration.
  2. custom_providers normalization now preserves more real-world config shapes.

    • id is accepted as a provider-name alias alongside name.
    • models can be expressed as either list[str] or list[dict].
    • Per-model metadata such as context_length is retained so downstream context resolution does not silently fall back.

Tests

  • scripts/run_tests.sh tests/cli/test_voice_record_key_config.py tests/hermes_cli/test_provider_config_validation.py tests/hermes_cli/test_custom_provider_context_length.py

Related Issues

  • Fixes #19915
  • Fixes #19857

Notes

  • Scope is intentionally limited to regression hardening and normalization behavior.
  • No provider-routing logic changes are included in this PR.

Changed files

  • cli.py (modified, +23/-7)
  • hermes_cli/config.py (modified, +28/-9)
  • tests/cli/test_voice_record_key_config.py (added, +17/-0)
  • tests/hermes_cli/test_custom_provider_context_length.py (modified, +22/-0)
  • tests/hermes_cli/test_provider_config_validation.py (modified, +30/-0)

Code Example

Error: Invalid key:

---

_raw_key = load_config().get("voice", {}).get("record_key", "ctrl+b")
if not _raw_key:          # catches "" as well as None/missing
    _raw_key = "ctrl+b"
_voice_key = _raw_key.lower().replace("ctrl+", "c-").replace("alt+", "a-")
RAW_BUFFERClick to expand / collapse

Bug Description

When voice.record_key is set to an empty string ("") in ~/.hermes/config.yaml, the interactive REPL (hermes) crashes immediately after the welcome banner with:

Error: Invalid key:

The crash occurs during prompt_toolkit keybinding registration in cli.py:run(). _voice_key is derived from load_config().get("voice", {}).get("record_key", "ctrl+b"), but .get() only fires the default when the key is absent — not when it is present with value "". The empty string propagates to kb.add(""), which raises ValueError("Invalid key: ") from prompt_toolkit/key_binding/key_bindings.py:455.

This bubbles up through cli.run()cli.main()cmd_chat() where it's caught by except ValueError as e: print(f"Error: {e}"). The error message looks like an API key authentication failure but is actually a keyboard-binding registration error.

Steps to Reproduce

  1. Set voice.record_key: "" in ~/.hermes/config.yaml
  2. Run hermes (interactive REPL)
  3. Observe: banner displays → "Welcome to Hermes Agent!" → tip → Error: Invalid key: → crash

Expected Behavior

An empty record_key should fall back to the default "ctrl+b" (or at minimum produce a clear error message indicating the config key is invalid, not an API key error).

Actual Behavior

REPL crashes with misleading "Invalid key" error that appears to be an API authentication failure. One-shot mode (hermes chat -q "test") works fine because it doesn't register prompt_toolkit keybindings. TUI also works because it has a separate code path.

Root Cause

In cli.py, three locations read voice.record_key via load_config().get("voice", {}).get("record_key", "ctrl+b"):

  • Line ~8579: voice mode activation display
  • Line ~8649: voice mode status display
  • Line ~10564: REPL keybinding registration (@kb.add(_voice_key))

The .get("record_key", "ctrl+b") default only applies when the key is absent from the config dict. When record_key: "" is explicitly set (e.g., intentionally for hands-free VAD mode per docs), the empty string is used directly, causing the crash at line ~10564.

Proposed Fix

Add a guard after reading _raw_key:

_raw_key = load_config().get("voice", {}).get("record_key", "ctrl+b")
if not _raw_key:          # catches "" as well as None/missing
    _raw_key = "ctrl+b"
_voice_key = _raw_key.lower().replace("ctrl+", "c-").replace("alt+", "a-")

Apply at all three sites.

Related

  • #11387: voice.record_key: alt+* crashes startup — same class of bug (invalid key format → prompt_toolkit ValueError) but different root cause.

Environment

  • OS: macOS
  • Hermes version: git-installed (HEAD: 026a5e47d)
  • Python: 3.11

extent analysis

TL;DR

The issue can be fixed by adding a guard to handle empty strings when reading the record_key configuration.

Guidance

  • Verify that the record_key is not empty before using it to register keybindings in cli.py.
  • Apply the proposed fix by adding a guard after reading _raw_key to handle empty strings and missing keys.
  • Update the code at all three locations where voice.record_key is read to ensure consistency.
  • Test the fix by setting voice.record_key to an empty string in ~/.hermes/config.yaml and running hermes to ensure it no longer crashes.

Example

_raw_key = load_config().get("voice", {}).get("record_key", "ctrl+b")
if not _raw_key:          # catches "" as well as None/missing
    _raw_key = "ctrl+b"
_voice_key = _raw_key.lower().replace("ctrl+", "c-").replace("alt+", "a-")

Notes

This fix assumes that an empty string is not a valid record_key and should fall back to the default value. If an empty string is a valid configuration, additional handling may be required.

Recommendation

Apply the proposed workaround by adding the guard to handle empty strings when reading the record_key configuration, as it directly addresses the root cause of the issue and prevents the crash.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING