Skip to content

fix: clamp importance to 1-5 and surface Mnemon CLI errors in tool response - #13

Merged
gitricko merged 1 commit into
mainfrom
fix-importance-error-handling
Jun 15, 2026
Merged

gitricko merged 1 commit into
mainfrom
fix-importance-error-handling

Conversation

@gitricko

Copy link
Copy Markdown
Owner
Summary

Two bugs in the mnemon plugin's mnemon_remember tool were found and fixed:

1. Silent error masking — when the mnemon CLI exits non-zero (e.g., validation failure, content too long), the plugin discarded stderr and returned {"success": false, "id": "error"} with no actionable detail. This made debugging impossible — the agent (and developer) saw only "error" with no clue why.

2. Out-of-range importance values were passed directly to the CLI — the tool schema correctly defines importance: {minimum: 1, maximum: 5}, but the code passed the raw integer to mnemon remember --imp N without clamping. Values like 8 caused the CLI to exit with code 1, which the wrapper then silently swallowed.



Root Cause


$ mnemon remember "test" --imp 8
Error: importance must be 1-5, got 8


The mnemon CLI v0.1.14 validates this constraint at the CLI layer (exit code 1, empty stdout, stderr carries the real message). The plugin's _run_mnemon wrapper returned (1, "", "...error..."), and the plugin discarded the stderr, producing the opaque {"success": false, "id": "error"} that appeared in sessions.

The same failure pattern occurs for any rc=1 condition:
- importance > 5 or < 1
- content > 8000 chars
- invalid --cat value
- any other CLI validation error



What Changed

mnemon/init.py

1. New instance attribute — init
python
self._last_remember_error: str | None = None

Stores the last CLI error message so it can be surfaced to the caller.

2. Clamp importance in _remember
python
importance = max(1, min(5, importance))

Before building the CLI arguments, importance is clamped to the valid 1–5 range. This prevents the CLI from ever receiving an out-of-range value. The clamping is at the Python layer so the agent still receives the corrected value at the CLI level.

3. Capture stderr on CLI failure — _remember
python
code, stdout, stderr = _run_mnemon(args, timeout=15)
if code != 0:
    logger.warning("mnemon remember failed (rc=%d): %s", code, stderr.strip()[:200])
    self._last_remember_error = stderr.strip()
    return None
self._last_remember_error = None

- stderr is now captured instead of discarded (_)
- Error is logged at WARNING level for observability
- Error message is stashed on self._last_remember_error for the caller
- On success, _last_remember_error is cleared

4. Propagate error to the agent — handle_tool_call
python
if not iid:
    err = getattr(self, "_last_remember_error", "") or "unknown error"
    return json.dumps({"success": False, "id": "error", "error": err})
return json.dumps({"success": True, "id": iid})

Before: {"success": false, "id": "error"} ← opaque  
After: {"success": false, "id": "error", "error": "importance must be 1-5, got 8"} ← actionable



tests/test_mnemon.py

Two new regression tests in TestToolCalls:

test_remember_error_reports_stderr — verifies that when the CLI exits non-zero, the tool response includes the actual error message:
python
mock_run.return_value = (1, "", "content too long (10000 chars, max 8000)")
result = self.p.handle_tool_call("mnemon_remember", {"text": "foo"})
data = json.loads(result)
self.assertFalse(data["success"])
self.assertEqual(data["id"], "error")
self.assertEqual(data["error"], "content too long (10000 chars, max 8000)")


test_remember_clamping — verifies that out-of-range importance values are silently corrected before the CLI is invoked:
python
self.p.handle_tool_call("mnemon_remember", {"text": "foo", "importance": 8})
called_args = mock_run.call_args[0][0]
self.assertEqual(called_args[called_args.index("--imp") + 1], "5")


All 38 tests pass (36 existing + 2 new).



Before / After

Scenario: importance: 8
Before: {"success": false, "id": "error"}
After: {"success": false, "id": "error", "error": "importance must be 1-5, got 8"} → agent self-corrects
────────────────────────────────────────
Scenario: content > 8000 chars
Before: {"success": false, "id": "error"}
After: {"success": false, "id": "error", "error": "content too long (10000 chars, max 8000)"}
────────────────────────────────────────
Scenario: importance: 8 (with clamping)
Before: CLI exits 1, agent retries blindly
After: Clamped to 5, CLI receives valid input, succeeds
────────────────────────────────────────
Scenario: Observability
Before: No log on failure
After: WARNING mnemon remember failed (rc=1): ... in logs



Testing

bash
python3 -m unittest discover -s tests -p "test_*.py"
Ran 38 tests in 0.006s — OK

@gitricko
gitricko merged commit 1e6ac62 into main Jun 15, 2026
7 checks passed
@gitricko
gitricko deleted the fix-importance-error-handling branch June 15, 2026 16:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant