decentraland / decentraland/builder-client

Filter directory entries and 0-byte files from zip extraction

Open
#79 0 comments 0 reactions 0 assignees View on GitHub
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.