open
https://gitlab.synchro.net/main/sbbs/-/work_items/1287
## Summary
`Archive.type` returns an error message as the type string for any archive with no entries:
| File | `Archive.type` | Expected |
|---|---|---|
| empty `.zip` | `"archive_read_next_header returned 1: (null)"` | `"ZIP"` |
| empty `.tar` | `"archive_read_next_header returned 1: (null)"` | `"tar"` |
| 0-byte file | `"archive_read_next_header returned 1: (null)"` | `null` |
Found while testing the #1278 fix (b958104a19, landing-13-joined, 2026-10-16). Not caused by it: HEAD before that commit behaves the same.
## Reproduce
```
$ python3 -c "import zipfile; zipfile.ZipFile('empty.zip','w').close()"
$ jsexec -n -r 'print(new Archive("empty.zip").type)'
archive_read_next_header returned 1: (null)
```
## Cause
`archive_type()` (`src/sbbs3/filedat.c`) reads the first header. On any result other than `ARCHIVE_OK` it writes an error message into the output string and returns the result code. The `Archive.type` getter (`js_archive_type()` in `src/sbbs3/js_archive.cpp`) treats any result `>= 0` as success and returns the string. "No entries" is `ARCHIVE_EOF`, which is `+1`, so the error message is returned as the type.
## Proposed fix
In `archive_type()`, treat `ARCHIVE_EOF` like `ARCHIVE_OK`: the format has already been detected by then, so report its name. Treat libarchive's "Empty file" format (`ARCHIVE_FORMAT_EMPTY`) as unrecognized, so a 0-byte file gives `null`.
Related: `Archive.list()` on a 0-byte file returns an empty array with no error, so a 0-byte file currently passes for an archive with no files. Rejecting `ARCHIVE_FORMAT_EMPTY` in `check_archive_entry()` would cover that too.
_Authored by Claude (Claude Code), on behalf of @rswindell_
--- SBBSecho 3.38-Linux
* Origin: Vertrauen - [vert/cvs/bbs].synchro.net (1:103/705)