skills-ref: validate crashes on Windows for any SKILL.md with non-ASCII characters (read_text without encoding)

Author: maestro01-33Created Aug 25, 2026Updated Aug 25, 2026

TL;DR

skill_md.read_text() is called without encoding=, so on Windows it decodes with the locale codepage (cp1252 here). Any SKILL.md containing a non-ASCII byte — an umlaut, an em dash, a curly quote, an arrow — crashes validate with an unhandled UnicodeDecodeError traceback instead of a validation result.

Repro

Windows 11, Python 3.14, skills-ref 0.1.1 from PyPI.

skill/
└── SKILL.md
markdown
---
name: n8n-workflows
description: Workflow-IDs, Namen und Ziel-Tabellen der n8n-Workflows. Nutzen bei jeder n8n-Arbeit.
---

# n8n

Übersicht — die Workflows schreiben nach Supabase.
> agentskills validate .\skill
Traceback (most recent call last):
  ...
  File "...\skills_ref\validator.py", line 172, in validate_skill
    content = skill_md.read_text()
  File "...\pathlib\__init__.py", line 793, in read_text
    return f.read()
  File "...\encodings\cp1252.py", line 23, in decode
    return codecs.charmap_decode(input, self.errors, decoding_table)[0]
UnicodeDecodeError: 'charmap' codec can't decode byte 0x9d in position 762: character maps to <undefined>

Setting PYTHONUTF8=1 works around it, which confirms the cause.

What is happening

Two call sites read SKILL.md with the platform default encoding:

  • skills-ref/src/skills_ref/parser.py:89content = skill_md.read_text()
  • skills-ref/src/skills_ref/validator.py:172content = skill_md.read_text()

On Linux and macOS the default is effectively UTF-8, so this never shows up there. On Windows, locale.getpreferredencoding(False) is the ANSI codepage (cp1252 on a German install, cp936 on Chinese, and so on), and the read fails on the first byte outside that codepage.

The failure mode is worse than it needs to be. validator.py wraps the read in except ParseError, so a UnicodeDecodeError is not caught and escapes as a raw traceback rather than a returned error string:

python
    try:
        content = skill_md.read_text()
        metadata, _ = parse_frontmatter(content)
    except ParseError as e:
        return [str(e)]

Why this matters

Non-ASCII in SKILL.md is not an edge case:

  • The spec itself describes name as "unicode lowercase alphanumeric characters", so non-ASCII is explicitly contemplated.
  • description is prose with a 1024-character budget, and the spec asks it to say what the skill does and when to use it. Skills written in German, French, Spanish, Turkish, Chinese, Japanese — anything but English — hit this immediately.
  • Even English-only skills trip it as soon as a , , or appears in the body, which is common in hand-written Markdown.

Validating a non-English skill on Windows is currently impossible without knowing to set PYTHONUTF8=1 first, and the traceback gives no hint that encoding is the problem.

Suggested fix

Pass the encoding explicitly at both call sites:

python
content = skill_md.read_text(encoding="utf-8")

Optionally also catch the decode failure so it is reported like any other validation problem rather than as a traceback:

python
    try:
        content = skill_md.read_text(encoding="utf-8")
        metadata, _ = parse_frontmatter(content)
    except UnicodeDecodeError as e:
        return [f"SKILL.md is not valid UTF-8: {e}"]
    except ParseError as e:
        return [str(e)]

If UTF-8 is the intended on-disk encoding for SKILL.md, it may be worth stating that in the specification too — it is currently implied rather than written down.

Happy to open a PR if that is useful.