mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-08 03:01:47 +00:00
fix(cli): catch OSError in _resolve_attachment_path to prevent ENAMETOOLONG dropping long slash commands
When the user pastes a long slash command like \`/goal <long prose>\` into
\`hermes chat\`, the input flows into \`_detect_file_drop()\`, whose
\`starts_like_path\` prefilter accepts anything starting with \`/\` and
forwards it to \`_resolve_attachment_path()\`. That helper calls
\`Path.exists()\` which invokes \`os.stat()\`, which raises
\`OSError(errno=ENAMETOOLONG)\` — 63 on macOS, 36 on Linux — when the
candidate exceeds NAME_MAX (typically 255 bytes).
The OSError propagates up to the broad \`except Exception\` in
\`process_loop\` (cli.py:11798), gets logged at WARNING level, and the
user's input is silently dropped. From the user's POV the chat prompt
hangs — the only signal is in agent.log:
WARNING cli: process_loop unhandled error (msg may be lost):
[Errno 63] File name too long: "/goal Drive the space board..."
This affects any slash command with prose-length arguments — \`/goal\`
in particular but also \`/skill\`, \`/cron\`, custom user commands.
Fix: wrap the \`exists()\`/\`is_file()\` calls in try/except OSError so
structurally-invalid path candidates cleanly return None. The slash-
command dispatch path downstream (cli.py:11718) then handles the
input correctly.
Tests: two new regression cases in test_cli_file_drop.py cover the
original \`/goal\` reproducer and a synthetic long path. All 35 file-
drop tests pass.
Reproducer (without the fix):
python -c "from cli import _detect_file_drop;
_detect_file_drop('/goal ' + 'a'*300)"
→ OSError: [Errno 63] File name too long
This commit is contained in:
parent
a0fedfbb1b
commit
906881c38b
2 changed files with 46 additions and 1 deletions
16
cli.py
16
cli.py
|
|
@ -1550,7 +1550,21 @@ def _resolve_attachment_path(raw_path: str) -> Path | None:
|
|||
except Exception:
|
||||
resolved = path
|
||||
|
||||
if not resolved.exists() or not resolved.is_file():
|
||||
# Path.exists() / is_file() invoke os.stat(), which raises OSError when
|
||||
# the candidate string is structurally invalid as a path — most commonly
|
||||
# ENAMETOOLONG (errno 63 on macOS, errno 36 on Linux) when the input
|
||||
# exceeds NAME_MAX (typically 255 bytes). This bites pasted slash
|
||||
# commands like `/goal <long prose>` because `_detect_file_drop()`'s
|
||||
# `starts_like_path` prefilter accepts any input starting with `/`,
|
||||
# then this resolver tries to stat it before short-circuiting on the
|
||||
# slash-command path. Without this guard the OSError propagates up to
|
||||
# the process_loop catch-all in _interactive_loop and the user input
|
||||
# is silently lost (the warning ends up in agent.log but the user sees
|
||||
# nothing — the prompt just hangs).
|
||||
try:
|
||||
if not resolved.exists() or not resolved.is_file():
|
||||
return None
|
||||
except OSError:
|
||||
return None
|
||||
return resolved
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue