A Bedrock skin pack is four files, and two of its failure modes are silent
Two UUIDs that must differ, a lang file that is not optional, and skin keys that strip punctuation — so 'My Skin!' and 'My Skin?' become one skin with no error.
A .mcpack is a zip with a different extension, and a skin pack inside one needs exactly four kinds of file:
manifest.json a header and a skin_pack module, each with its own UUID
skins.json one entry per skin: texture, geometry, and a KEY
texts/en_US.lang the display names — skins.json holds only keys
*.png the textures themselves
Three of the four are easy to get right. The interesting failures all come from the relationship between them.
The two UUIDs must differ
manifest.json carries a UUID in its header and another in its skin_pack module:
{
"format_version": 1,
"header": { "name": "…", "uuid": "…", "version": [1, 0, 0] },
"modules": [ { "type": "skin_pack", "uuid": "…", "version": [1, 0, 0] } ]
}
They are two separate identities — the pack, and the thing inside the pack — and reusing one value for both is a real error even though nothing complains at build time.
There is a second, subtler consequence: a re-import with the same UUIDs is treated as the same pack, and Minecraft silently keeps the copy it already has. Editing a pack, rebuilding it with the same identifiers and re-importing looks like it did nothing. Every build from this page gets fresh UUIDs for exactly that reason, which is also why re-importing an edited pack needs a fresh download rather than a re-zip of the old one.
The lang file is not optional
skins.json never contains a display name. It contains a key, and texts/en_US.lang maps that key to text:
skinpack.<PackKey>=My Pack
skin.<PackKey>.<SkinKey>=My First Skin
Ship the pack without the lang file and nothing errors — every skin simply appears in the game under its raw key. This is the most common reason a hand-assembled pack looks broken while validating fine.
Keys strip punctuation, and duplicates collapse
A key must be alphanumeric. Everything else is removed:
| your name | key |
|---|---|
My Skin! | MySkin |
My Skin? | MySkin |
Skin #2 | Skin2 |
!!! | Skin1 (fallback, by position) |
The first two rows are the trap. Two skins named distinctly by a human become one key, and a duplicate key does not error — it silently collapses into a single skin in game. You upload twelve skins, get eleven, and nothing tells you which one went missing.
The builder here defends against that by appending a counter to any key it has already used, so the second MySkin becomes MySkin1. Worth knowing if you assemble a pack by hand: the check you need is on the stripped names, not the ones you typed.
A name that is entirely punctuation leaves nothing at all, so it falls back to Skin plus its position in the list.
Slim needs the right geometry string
Each entry names a model, and there are two:
geometry.humanoid.custom— classic, 4-pixel armsgeometry.humanoid.customSlim— slim, 3-pixel arms
Getting this wrong is the usual cause of an Alex-proportioned skin turning up with Steve's blocky arms. The texture is fine; the model is the one being asked for. The pack builder infers it by sampling the fourth arm column, but a hand-written skins.json has to say it explicitly, and the two strings differ by four characters at the end.
Entries also carry "type": "free", and a cape is an optional extra PNG referenced from the same entry — the cape travels inside the skin pack rather than as a separate download.