box / box/boxcli

`folders:download --zip` produces an empty archive (or hangs) since v4.10.0: this.zip is assigned after traversal starts

Open
#718 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
JavaScript
Stars
284
Forks
67
Avg merge
6h 45m
Merged PRs (30d)
4

Description

### Description

Since v4.10.0, `box folders:download --zip` writes a 0-byte `.zip` and puts the downloaded files loose next to it (or hangs), instead of producing an archive. v4.8.2 produces a valid archive for the same folder.

### Root cause

#686 moved `folders:download` to `archiver` 8, which is ESM-only, so `_setupZip()` became `async` and now awaits a dynamic `import('archiver')` before assigning `this.zip`:

```js
// src/commands/folders/download.js (v4.10.0)
outputFinalized = this._setupZip(path.join(destinationPath, fileName)); // not awaited
...
for await (let item of this._getItems(id, '')) {
if (item.type === 'folder' && !this.zip) { ... mkdirp ... }
else if (item.type === 'file') {
if (this.zip) { this.zip.append(stream, { name: item.path }); }
else { await saveFileToDisk(destinationPath, item, stream); }
}
}
if (this.zip) { this.zip.finalize(); }
await outputFinalized;
```

`run()` starts traversing the folder without waiting for the import, so whenever the first folder listing comes back before the module has loaded, `this.zip` is still `undefined`: the items take the non-zip path (folders are created on disk, files are saved to disk), `finalize()` is skipped because `this.zip` is checked before the import resolves, and the write stream for the archive is never finalized. Depending on timing the process either exits with an empty `.zip` or waits forever on `outputFinalized`.

Simply awaiting `_setupZip()` is not a fix: it returns the promise that resolves when the output stream closes, so awaiting it before traversal deadlocks.

`box files:zip` is not affected (it uses the server-side zip download API).

### Steps to reproduce

1. Point the CLI at a local mock of the API (`apiRootURL` in `~/.box/settings.json`) that serves one folder containing one file, or use a real small folder.
2. `box folders:download --zip --destination out -t `
3. Inspect `out/`.

Observed with v4.10.0 on Node 20.20.1 (5/5 runs): `folders-download--....zip` is 0 bytes, and `out//` exists on disk. Same steps with v4.8.2: valid archive containing `/`, no loose files. On Node 24 the import happens to win the race, so the bug can look intermittent across environments.

### Versions

- `@box/cli` 4.10.0 (`archiver` 8.0.0, `box-node-sdk` 4.14.0)
- Node 20.20.1, macOS arm64 (on Node 24 the import happens to win the race and the command succeeds)

### Fix

Load the archiver module before starting traversal, and keep `_setupZip()` synchronous so `this.zip` is assigned before the first item is processed. I have a PR with this change plus a regression test that asserts `this.zip` is set before `_getItems()` runs and that the archive contains the expected entries.

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.