hermes - ✅(Solved) Fix Corrupt webhook_subscriptions.json is silently treated as empty and overwritten on subscribe [2 pull requests, 1 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#14194Fetched 2026-04-23 07:46:13
View on GitHub
Comments
0
Participants
1
Timeline
5
Reactions
0
Participants
Timeline (top)
labeled ×3cross-referenced ×2

If ~/.hermes/webhook_subscriptions.json is corrupt or truncated, _load_subscriptions() silently returns {}. The next hermes webhook subscribe ... call treats that empty dict as real state and overwrites the file, discarding all existing subscriptions.

Error Message

try: data = json.loads(path.read_text(...)) return data if isinstance(data, dict) else {} except Exception: return {}

Root Cause

If ~/.hermes/webhook_subscriptions.json is corrupt or truncated, _load_subscriptions() silently returns {}. The next hermes webhook subscribe ... call treats that empty dict as real state and overwrites the file, discarding all existing subscriptions.

Fix Action

Fixed

PR fix notes

PR #14201: fix(cli): refuse to overwrite corrupt webhook subscriptions

Description (problem / solution / changelog)

Summary

  • surface invalid webhook_subscriptions.json contents instead of treating them as empty state
  • block hermes webhook commands from mutating a corrupted subscriptions file
  • add regression coverage for both the parse error and the subscribe no-overwrite path

Root cause

_load_subscriptions() swallowed every read/parse failure and returned {} for both a missing file and a corrupted one. hermes webhook subscribe then treated that empty dict as real state and overwrote the corrupted file.

Fix

  • make _load_subscriptions() raise a ValueError for unreadable, invalid-JSON, or non-object subscription files
  • catch that error in webhook_command() and print a user-facing error instead of continuing with the mutation path

Regression coverage

  • assert _load_subscriptions() raises on a corrupted subscriptions file
  • assert hermes webhook subscribe reports the error and leaves the corrupted file unchanged

Testing

  • scripts/run_tests.sh tests/hermes_cli/test_webhook_cli.py
  • scripts/run_tests.sh tests/hermes_cli (existing unrelated failures on current main: ZAI provider URL expectations, gateway WSL systemd checks, and one oversized tip string)

Closes #14194

Changed files

  • hermes_cli/webhook.py (modified, +27/-8)
  • tests/hermes_cli/test_webhook_cli.py (modified, +16/-1)

PR #14252: fix(cli): block corrupt webhook subscription rewrites

Description (problem / solution / changelog)

Summary

  • treat missing webhook subscription stores as empty, but raise a dedicated error for unreadable, corrupt, or non-object JSON files
  • surface that store error through the CLI instead of silently replacing the corrupted file during subscribe/remove/list/test flows
  • add regressions proving corrupt JSON is reported and left untouched during subscribe

Testing

  • python3 -m pytest -o addopts= tests/hermes_cli/test_webhook_cli.py -k "corrupted_file or overwrite_corrupted"
  • python3 -m pytest -o addopts= tests/hermes_cli/test_webhook_cli.py

Closes #14194

Changed files

  • hermes_cli/webhook.py (modified, +35/-11)
  • tests/hermes_cli/test_webhook_cli.py (modified, +15/-2)

Code Example

try:
    data = json.loads(path.read_text(...))
    return data if isinstance(data, dict) else {}
except Exception:
    return {}
RAW_BUFFERClick to expand / collapse

Summary

If ~/.hermes/webhook_subscriptions.json is corrupt or truncated, _load_subscriptions() silently returns {}. The next hermes webhook subscribe ... call treats that empty dict as real state and overwrites the file, discarding all existing subscriptions.

Affected files

  • hermes_cli/webhook.py:36-44
  • hermes_cli/webhook.py:47-55
  • hermes_cli/webhook.py:142-162

Why this is a bug

_load_subscriptions() swallows every exception:

try:
    data = json.loads(path.read_text(...))
    return data if isinstance(data, dict) else {}
except Exception:
    return {}

That makes "missing file" and "corrupt file" indistinguishable. A later mutating command loads {}, adds one subscription, and _save_subscriptions() atomically replaces the original file.

Minimal reproduction

  1. Write invalid JSON into ~/.hermes/webhook_subscriptions.json
  2. Run hermes webhook subscribe demo --events ping

Expected

Hermes should surface a parse error and refuse to modify the corrupted subscriptions file until it is repaired (or require an explicit force/recovery path).

Actual

Hermes treats the corrupted file as empty state and rewrites it with only the new subscription, causing silent data loss.

Suggested investigation

Differentiate between:

  • file missing → return {}
  • file unreadable / invalid JSON → report an error and block mutating operations

A regression test should verify that invalid JSON does not get silently replaced by _cmd_subscribe().

extent analysis

TL;DR

The issue can be fixed by modifying the _load_subscriptions() function to differentiate between a missing file and a corrupt file, and to report an error when the file contains invalid JSON.

Guidance

  • Modify the _load_subscriptions() function to catch specific exceptions instead of the general Exception class, allowing it to distinguish between different error cases.
  • Add a check to verify if the file exists before attempting to read it, and return an error if it does not exist or if the JSON is invalid.
  • Implement a regression test to ensure that _cmd_subscribe() does not silently replace invalid JSON with an empty subscription list.
  • Consider adding a force or recovery option to allow users to explicitly overwrite a corrupted subscriptions file.

Example

try:
    data = json.loads(path.read_text(...))
    if not isinstance(data, dict):
        raise ValueError("Invalid subscription data")
    return data
except FileNotFoundError:
    return {}  # or raise a custom error
except json.JSONDecodeError as e:
    raise ValueError(f"Invalid JSON: {e}")
except Exception as e:
    raise ValueError(f"Error loading subscriptions: {e}")

Notes

The provided code snippet is a minimal example and may need to be adapted to fit the specific requirements of the hermes application. Additionally, the exact implementation details may vary depending on the desired behavior when encountering a corrupted subscriptions file.

Recommendation

Apply a workaround by modifying the _load_subscriptions() function to handle specific exceptions and report errors for invalid JSON, as this will prevent silent data loss and provide a more robust user experience.

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