Codex macOS app previews .skill ZIP packages as UTF-8 text mojibake
Bug
Codex macOS app local file preview treats a .skill package as plain text and renders ZIP bytes as mojibake/replacement characters.
This is not file corruption: the .skill file is a valid ZIP archive.
Environment
- App: Codex macOS app
- Bundle id:
com.openai.codex - App version:
26.513.31313 - Build:
2867 - Codex CLI:
codex-cli 0.130.0 - OS: macOS 26.3.1
Repro
- Open a local project containing a
.skillpackage. - In Codex macOS app, open/preview a file like:
dist/skills/math-handoff-skill/math-handoff-skill.skill
Actual behavior
The file preview opens a text/code view and shows binary ZIP data as mojibake. The rendered content starts with PK and then many replacement characters / corrupted-looking text.
<img width="575" height="705" alt="Image" src="https://github.com/user-attachments/assets/6835ded7-e04d-49fb-a77e-f3af4c3c9e1d" />
Expected behavior
Codex should not render binary/archive files as UTF-8 text. It should show a binary/archive placeholder, open externally, or expose archive contents.
File validation
The file is valid ZIP data:
- first bytes:
50 4b 03 04 zipfile.is_zipfile(...) == trueunzip -tsucceeds- entries include:
math-handoff-skill/skill-boundary.jsonmath-handoff-skill/SKILL.mdmath-handoff-skill/templates/handoff.md
It is invalid UTF-8:
- first UTF-8 decode error at byte 11
- lossy decode produces many
U+FFFDreplacement chars
Diagnosis
The app appears to classify local workspace files by extension before choosing preview behavior.
The packaged app bundle has an unsupported/archive extension list that includes extensions like zip, jar, tar, gz, rar, etc., but .skill is not included. Therefore .skill falls through to the plain-text preview path.
The read-file handler then decodes bytes as UTF-8 text. Invalid bytes are replaced with U+FFFD, and the file-preview page renders the resulting string.
So the immediate failure seems to be in the Codex macOS app file-preview classification/decoding path, not in the .skill package itself.
Suggested fixes
Minimal:
- classify
.skillas an archive/unsupported preview type, same as.zip.
Robust:
- perform binary detection before text rendering:
- strict UTF-8 decode via
TextDecoder('utf-8', { fatal: true }) - detect NUL bytes/control-byte ratio
- detect common magic bytes, including ZIP
PK\x03\x04 - only pass
contents: stringto the text renderer when the file is text-safe.
Broader:
- return
isBinary/mimeType/encodingmetadata from file-read APIs and let the preview page choose the correct renderer.