skills-ref: validate crashes on Windows for any SKILL.md with non-ASCII characters (read_text without encoding)
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---
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:89—content = skill_md.read_text()skills-ref/src/skills_ref/validator.py:172—content = 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:
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
nameas "unicode lowercase alphanumeric characters", so non-ASCII is explicitly contemplated. descriptionis 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:
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:
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.
Source: agentskills/agentskills