decentraland / decentraland/builder-client
Filter directory entries and 0-byte files from zip extraction
- Dominant language
- TypeScript
- Stars
- 3
- Forks
- 4
- PR merge metrics
- No merged PRs in 30d
Description
## Problem
When a creator uploads a smart wearable zip, the zip parser includes **directory entries** (e.g. `bin/`) and **empty (0-byte) files** in the extracted file list. These end up stored in `item.contents` and `representations[].contents` in the Builder API.
When the curator later deploys the collection, the Catalyst content-validator rejects the entity because:
- Directory entries like `male/bin/` are listed in `representation.contents` but are not valid content files
- 0-byte files (e.g. `main.crdt`, bundler artifacts) are listed in metadata but silently dropped from the entity content payload, creating a mismatch
The error surfaced is:
```
Deploy failed with status 400: Representation content: 'male/bin/' is not one of the content files
```
Removing the failing entry just moves the error to the next problematic file.
## Root cause
In `src/files/files.ts`, the `zip.forEach` loop iterates over all zip entries without filtering directory entries or 0-byte files:
```ts
zip.forEach((filePath, file) => {
if (
!basename(filePath).startsWith('.') &&
basename(filePath) !== WEARABLE_MANIFEST &&
...
) {
fileNames.push(filePath) // ← 'bin/' and empty files get included
}
})
```
JSZip exposes directory entries (where `file.dir === true`) alongside regular files, and the current filter does not exclude them. Empty files share the CID `bafkreihdwdcefgh4dqkjv67uzcmw7ojee6xedzdetojuzjevtenxquvyku` (hash of 0 bytes) and cause the same mismatch downstream.
## Proposed fix
Add `!file.dir` and a 0-byte size check to the `zip.forEach` filter:
```ts
zip.forEach((filePath, file) => {
if (
!file.dir && // skip directory entries
file._data?.uncompressedSize !== 0 && // skip 0-byte files
!basename(filePath).startsWith('.') &&
basename(filePath) !== WEARABLE_MANIFEST &&
...
) {
fileNames.push(filePath)
}
})
```
Fixing this at the source means the data stored in the Builder API is already correct, and no downstream consumer needs to compensate.
## Related
A companion defensive fix is needed in `decentraland/builder` to strip 0-byte entries from `representations[].contents` at deploy time for items already stored with this bad data.
Requested by Rocío Corral Mena via Slack
Contributor guide
Assessment
This issue has not been assessed yet.