Skills bundle hash crashes on bytes content

NousResearch/hermes-agent · cluster 2739 · hermes cli

Data as of

Nothing open on GitHub. Fix merged in #19575. Check it covers your case.

The short answer

0open issues
0open PRs
17items grouped
Known?
Yes. 17 related items grouped here, 4 issues already closed.
Fix event
A fix was merged to main in PR #19575. Check it covers your case. No release data is tracked.
Tracker
The maintainers' tracker marks this partially resolved, deciding PR #19575. That is separate from the fix event.
Still open
Nothing open on GitHub.

A bug where bundle_content_hash raises an AttributeError when encountering bytes values during skills check/update operations. Multiple fix attempts address handling bytes alongside string content in the hashing logic.

Open issues0

No open issues.

Open PRs0

No open pull requests.

Fixes on record

Merged to main. A stamp counts the issues a fix is linked to.

  • #19575 fix(skills): hash byte-backed bundle files (closes #13408)

    merged to main

    files: scripts/release.py, tests/tools/test_skills_hub.py, tools/skills_hub.py

    Show the diff
    • scripts/release.py
      @@ -633,6 +633,8 @@
           "[redacted:email]": "CIRWEL",
           "[redacted:email]": "molvikar",
           "[redacted:email]": "nftpoetrist",
      +    "[redacted:email]": "colorcross",
      +    "[redacted:email]": "zhao0112",
           "[redacted:email]": "leozeli",
           "[redacted:email]": "LehaoLin",
           "[redacted:email]": "I3eg1nner",
    • tests/tools/test_skills_hub.py
      @@ -901,6 +901,69 @@ def test_bundle_content_hash_matches_installed_content_hash(self, tmp_path):
       
               assert bundle_content_hash(bundle) == content_hash(skill_dir)
       
      +    def test_bundle_content_hash_accepts_binary_files(self):
      +        bundle = SkillBundle(
      +            name="demo-binary-skill",
      +            files={
      +                "SKILL.md": "# Demo\n",
      +                "assets/logo.png": b"\x89PNG\r\n\x1a\nbinary",
      +            },
      +            source="github",
      +            identifier="owner/repo/demo-binary-skill",
      +            trust_level="community",
      +        )
      +
      +        digest = bundle_content_hash(bundle)
      +
      +        assert digest.startswith("sha256:")
      +
      +    def test_bundle_content_hash_bytes_matches_str_equivalent(self):
      +        """Bytes content must hash identically to its str-decoded form."""
      +        text_bundle = SkillBundle(
      +            name="demo-skill",
      +            files={
      +                "SKILL.md": "same content",
      +                "references/checklist.md": "- [ ] security\n",
      +            },
      +            source="github",
      +            identifier="owner/repo/demo-skill",
      +            trust_level="community",
      +        )
      +        bytes_bundle = SkillBundle(
      +            name="demo-skill",
      +            files={
      +                "SKILL.md": b"same content",
      +                "references/checklist.md": b"- [ ] security\n",
      +            },
      +            source="github",
      +            identifier="owner/repo/demo-skill",
      +            trust_level="community",
      +        )
      +
      +        assert bundle_content_hash(bytes_bundle) == bundle_content_hash(text_bundle)
      +
      +    def test_bundle_content_hash_mixed_matches_on_disk(self, tmp_path):
      +        """In-memory bundle hash must equal on-disk content_hash for mixed bytes+str."""
      +        from tools.skills_guard import content_hash
      +
      +        bundle = SkillBundle(
      +            name="demo-skill",
      +            files={
      +                "SKILL.md": b"# Demo Skill\n",
      +                "references/checklist.md": "- [ ] security\n",
      +            },
      +            source="github",
      +            identifier="owner/repo/demo-skill",
      +            trust_level="community",
      +        )
      +        skill_dir = tmp_path / "demo-skill"
      +        skill_dir.mkdir()
      +        (skill_dir / "SKILL.md").write_bytes(b"# Demo Skill\n")
      +        (skill_dir / "references").mkdir()
      +        (skill_dir / "references" / "checklist.md").write_text("- [ ] security\n")
      +
      +        assert bundle_content_hash(bundle) == content_hash(skill_dir)
      +
           def test_reports_update_when_remote_hash_differs(self):
               lock = MagicMock()
               lock.list_installed.return_value = [{
    • tools/skills_hub.py
      @@ -2801,7 +2801,11 @@ def bundle_content_hash(bundle: SkillBundle) -> str:
           """Compute a deterministic hash for an in-memory skill bundle."""
           h = hashlib.sha256()
           for rel_path in sorted(bundle.files):
      -        h.update(bundle.files[rel_path].encode("utf-8"))
      +        content = bundle.files[rel_path]
      +        if isinstance(content, bytes):
      +            h.update(content)
      +        else:
      +            h.update(content.encode("utf-8"))
           return f"sha256:{h.hexdigest()[:16]}"
       
       

12 PRs closed without merging

From the close comments: done elsewhere 6 · duplicate 4 · merged via another change 1 · other 1

  • #2740 fix(skills): handle bytes content in bundle_content_hash · done elsewhere → 3072e55, 91ea3ae, #9925
  • #3751 Handle byte-backed skill bundle hashes · done elsewhere → 3072e55
  • #12518 fix(skills_hub): handle bytes content in bundle_content_hash · duplicate → #2740
  • #13424 fix(skills_hub): handle bytes content in bundle_content_hash · duplicate → #2740
  • #13531 fix(skills): handle bytes in bundle content hash · other → #3751
Show 7 more
  • #14108 fix(skills): hash byte-backed bundle files · duplicate → #13531
  • #19079 fix: handle bytes values in bundle_content_hash() · done elsewhere → #19575, #19328
  • #19081 fix(skills): handle bytes values in bundle_content_hash · done elsewhere → #19575, #19328
  • #19116 fix(skills): hash bytes bundle contents · duplicate → #9925, #14108, #13531, #13408
  • #19120 fix(skills): handle bytes values in bundle_content_hash — fixes AttributeError on skills check/update (#19090) · done elsewhere → #19575, #19328
  • #19145 fix(skills): handle bytes in bundle update hash · done elsewhere → #19575, #19328
  • #19328 fix(skills): hash byte-backed bundle files (closes #13408) · merged via another change → #19575
Closed issues · 4
  • Closed#2739[Bug]: `hermes skills update` fails
  • Closed#13408fix: bundle_content_hash crashes with bytes values (AttributeError)
  • Closed#19073AttributeError in bundle_content_hash when checking skill updates
  • Closed#19090Bug: hermes skills check/update crashes with AttributeError in bundle_content_hash
How this cluster was grouped
  • Link evidence (partly structural links): some members are connected by structural evidence (shared changed files, or a closing/superseded reference); the rest are joined by an automated relevance judgement over their text.
  • Members outside that structural core may belong to a different underlying problem.
  • Pairwise link density 0.14: the share of item pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. 1.00 means every pair is linked.

Pick up a Lab task

Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.

Open the Lab

At a glance

Subsystem
hermes cli
Merged PRs
#19575
Closed issues
4
Attempts
12 PRs closed without merging
Fix link checked
4 May 2026

For your agent

The same facts, typed.