Skip to content

fix(skills): tolerate non-UTF-8 bytes in hub lock.json (#68053) - #68062

Closed
PRATHAMESH75 wants to merge 1 commit into
NousResearch:mainfrom
PRATHAMESH75:fix/hub-lock-tolerate-non-utf8
Closed

fix(skills): tolerate non-UTF-8 bytes in hub lock.json (#68053)#68062
PRATHAMESH75 wants to merge 1 commit into
NousResearch:mainfrom
PRATHAMESH75:fix/hub-lock-tolerate-non-utf8

Conversation

@PRATHAMESH75

Copy link
Copy Markdown
Contributor

What does this PR do?

tools/skill_usage.py::_read_hub_installed_names() reads ~/.hermes/skills/.hub/lock.json with a strict UTF-8 decode: json.loads(lock_path.read_text(encoding="utf-8")). Hub skill descriptions can contain Windows-1252 typographic characters (em-dash 0x97, smart quotes, bullets) written as single high bytes. read_text(encoding="utf-8") then raises UnicodeDecodeError.

UnicodeDecodeError is a ValueError sibling — it is not caught by the function's except (OSError, json.JSONDecodeError), so it escapes and propagates up through web_server.py's get_skills handler, returning HTTP 500 for the entire /api/skills endpoint. The result: the desktop Capabilities/Skills panel goes blank and users can't see or toggle any skill, all because of one bad byte in a description field.

This decodes the lock file with errors="replace", so the offending byte degrades to U+FFFD and the structurally valid JSON — including every other skill name — stays readable. This heals both existing corrupted lock files and any written in the future, regardless of where the cp1252 byte originated.

Related Issue

Fixes #68053

(Addresses Problem 1 — the /api/skills 500. Problem 2 in that issue, stale hermes-agent.broken-* backup-dir cleanup, is a separate concern and out of scope for this focused fix.)

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)

Changes Made

  • tools/skill_usage.py_read_hub_installed_names() now reads lock.json with read_text(encoding="utf-8", errors="replace") instead of a strict decode, with a comment explaining why UnicodeDecodeError would otherwise escape the handler.
  • tests/tools/test_hub_lock_non_utf8_68053.py — regression tests: a lock file with a raw 0x97 em-dash byte no longer raises and the skill name is still recovered; a clean UTF-8 lock still reads unchanged.

How to Test

scripts/run_tests.sh tests/tools/test_hub_lock_non_utf8_68053.py

Result: 2 passed. Reverting the errors="replace" change makes test_windows_1252_em_dash_does_not_raise fail with the exact UnicodeDecodeError: 'utf-8' codec can't decode byte 0x97 from the issue traceback. Existing tests/tools/test_skill_usage.py (47 tests) still pass.

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix
  • I've run the affected tests and they pass
  • I've added tests for my changes
  • I've tested on my platform: macOS 15 (Darwin 25.5)

Documentation & Housekeeping

  • Documentation — N/A (internal reader hardening; behavior for valid files unchanged)
  • cli-config.yaml.example — N/A
  • CONTRIBUTING.md/AGENTS.md — N/A
  • Cross-platform impact considered — the corruption originates from Windows-1252 bytes; the fix makes the reader tolerant on every platform
  • Tool descriptions/schemas — N/A

_read_hub_installed_names() reads ~/.hermes/skills/.hub/lock.json with a
strict utf-8 decode. Hub skill descriptions can carry Windows-1252
typographic bytes (em-dash 0x97, smart quotes, bullets) as single high
bytes; read_text(encoding="utf-8") then raises UnicodeDecodeError, which
is a ValueError sibling not caught by the function's
except (OSError, json.JSONDecodeError). It escapes and 500s the whole
/api/skills endpoint, blanking the desktop Skills panel.

Decode with errors="replace" so the offending byte degrades to U+FFFD
and the structurally valid JSON — and every other skill — stays readable.

Fixes NousResearch#68053
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists tool/skills Skills system (list, view, manage) labels Jul 20, 2026

@Bryntly Bryntly left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. The use of errors='replace' effectively mitigates the UnicodeDecodeError by replacing invalid UTF-8 bytes like Windows-1252 typographics with the Unicode Replacement Character (U+FFFD). This allows json.loads to parse the structure successfully instead of crashing the API endpoint. Tests are comprehensive and CI is green.

@teknium1

Copy link
Copy Markdown
Contributor

Merged via PR #71078 — your commit(s) were cherry-picked onto current main with your authorship preserved in git log (rebase merge). This PR was part of the class-wide close-out of bare read_text/write_text calls: all 139 remaining sites now pass explicit encoding, and a new CI linter rule prevents regressions. Thanks for the contribution!

@teknium1 teknium1 closed this Jul 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P2 Medium — degraded but workaround exists tool/skills Skills system (list, view, manage) type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: corrupted .hub/lock.json with Windows-1252 bytes breaks desktop skills panel + stale crash backup directory

4 participants