Release notes
Release history for the Bugsee CLI (bugsee-cli). To install or upgrade, see
Installation and Updating the CLI. The
Android Gradle plugin and the iOS build scripts keep the CLI they manage up to
date for you.
0.8.x
0.8.1 (October 8 2026)
A native library that was first uploaded as a symbol table now upgrades to full
debug info by itself, with no --force.
-
--type elfupgrades a stored symbol table to DWARF automatically. A library first uploaded with the Android Gradle plugin'sndk.debugSymbolLevel = 'SYMBOL_TABLE'(a.so.sym, function names only) and later available with debug info shares one GNU build-id. The server used to treat the richer file as already present and skip it, and--force— which re-sends every library in the run — was the only way to replace it. Now each library declares whether it carries debug info or only a symbol table, read from the file itself and not its name, and the server replaces a poorer stored copy:You upload The server holds Result Library with debug info A symbol table Replaced. The file transfers once; the run logs upgraded SYMBOL_TABLE -> FULLand reportsupgraded=Nin its summary.The same file again The same file Skipped, nothing transfers. A symbol table Debug info Skipped, nothing transfers. A symbol is never downgraded. Directory uploads (
merged_native_libs/<variant>) benefit most: unchanged prebuilt libraries (the C++ runtime, React Native's own.sofiles) still transfer nothing, and only the one that gained debug info is sent.--forcestill means "always replace", and the library's richness is still recorded when you use it. See Native (ELF).Things to know:
- This needs the matching Bugsee server update. Against a server without it the
CLI behaves as in 0.8.0 — the extra fields are ignored — and a full-debug
library that was skipped still gets the "re-run with
--force" hint. A current server no longer prints that hint, because it would be wrong. - Only
--type elfsends the new fields; every other upload type is unchanged. --type rustdoes not take part yet, so a Rust ELF still needs--forceto go from stripped to unstripped.- A library that carries only dynamic symbols is classed as a symbol table, so a
later true
SYMBOL_TABLEupload for it will not replace it. That can miss an upgrade but never loses a symbol.
- This needs the matching Bugsee server update. Against a server without it the
CLI behaves as in 0.8.0 — the extra fields are ignored — and a full-debug
library that was skipped still gets the "re-run with
0.8.0 (October 7 2026)
Native symbols can be uploaded straight from a directory of libraries, a corrupt IL2CPP line map is caught before it is uploaded, uploads and source-map handling now run in a small, fixed amount of memory however large the files are, and four bugs found by a new round of tests on damaged input files are fixed.
-
--type elfaccepts directories. Point it at one or more directories — for example the Android Gradle plugin'sbuild/intermediates/merged_native_libs/<variant>— and it walks them recursively for.so,.so.dbgand.so.symfiles (plus anything you add with--extension), reading the libraries in place instead of requiring a pre-builtnative-debug-symbols.zip. A zip still works, and you can mix directories and zips in one command. Each library is uploaded as its own symbol, keyed by its GNU build-id (--uuid, the SDKBUILD_UUID, is still required and only correlates logs). When several files share a build-id (across all the paths you pass) only the richest is uploaded: DWARF first, then a symbol table, then the larger file. See Native (ELF).Directory input has a few rules worth knowing:
- A directory that contains no libraries exits
10rather than succeeding, so a mis-wired path or a task that ran before the libraries were built can't pass silently. An empty zip still only warns, as before. - Directory symlinks are not followed; a symlink to a library file is read.
- Any I/O error while scanning (an unreadable subdirectory or library, a dangling
link) fails the run with exit
11instead of uploading a partial set. - Libraries are memory-mapped while they are scanned, so the directory must hold finished build output. Don't run the upload while a linker is still writing into it.
- A directory that contains no libraries exits
-
Corrupt IL2CPP line maps are rejected before upload.
debug-files upload --type il2cpp-linemapnow checksLineNumberMappings.jsonagainst the shape the symbolicator reads (cpp_path→cs_path→cpp_line: cs_line, with non-negative integer line numbers; the optional__debug-id__entry is ignored). A truncated, empty, wrong or otherwise damaged file exits11with a message naming the file and the problem — including in a dry run — and uploads nothing. Before, it was uploaded successfully and every IL2CPP crash then failed to symbolicate with nothing pointing back at the upload. An empty map is still accepted, with a warning. See Unity IL2CPP. -
Uploads and symbol reads use a small, fixed amount of memory. The CLI used to read whole files into memory in several places, which mattered for the artefacts and symbol files it routinely handles. It now streams them. Peak memory, measured on large inputs:
Operation 0.7.13 0.8.0 upload build(300 MB artefact + 200 MB mapping)661 MB 32 MB --type proguard(200 MB mapping)241 MB 32 MB --type elf(zip of four 100 MB libraries)463 MB 36 MB --type sourcemaps(89 MB map)274 MB 32 MB sourcemaps inject(150 MB bundle)317 MB 2 MB Stripping an 89 MB map with
--strip-sources-contentnow takes about 32 MB, andsourcemaps injectof the same map about 6 MB. Figures are peak memory on large synthetic inputs.Nothing changes in what is uploaded, and debug IDs are identical.
-
sourcemaps injectedits files in place and refuses an unusable map first. The debug-ID stub is appended to the bundle without copying it, and the map is rewritten through the same file, so permissions, symlinks and hard links are kept. If a bundle's map can't take the ID — it isn't a JSON object, it is invalid, or it is read-only —injectnow fails before touching the bundle, so a failed run no longer leaves a bundle stamped with an ID its map never got. Two byte-level differences, both still valid maps:injectwritesdebug_idanddebugIdafter the map's other keys, and a map uploaded with--strip-sources-contentkeeps its original key order. -
--strip-sources-contentcan no longer silently fail to strip. If a map can't be processed, the upload logs a warning that the map is going out unstripped instead of passing without comment. Unusual but valid JSON numbers (such as1e100) no longer prevent stripping. -
Exit codes that changed. Check these if you script around the CLI:
--type il2cpp-linemapwith a corruptLineNumberMappings.json:11(was0).--type elfwith a path that does not exist:10(was11).--type elfcollects every input before uploading anything, so a corrupt second zip now fails (11) before the first zip uploads rather than after.
-
Fixed: a Mach-O with no UUID was reported as the all-zero UUID.
dsym uuidanddebug-files upload --type dsymprinted and registered00000000-0000-0000-0000-000000000000for a slice without anLC_UUID; such a slice can never match a crash report and every one would collide on the same key. It is now skipped, and a bundle with no usable slice is rejected — which fails anxcode upload-dsymsbuild phase, like any other unreadable bundle. -
Fixed: non-ELF files were accepted as ELF. A macOS-built
.sofound by a directory scan would have been uploaded as anelfsymbol keyed by its Mach-O UUID. A file that is not an ELF now has no build-id and is skipped with the usual warning. -
Fixed: a crash on a deeply nested
Info.plist.build-env read-plist(and thexcodecommands that readInfo.plist) aborted the whole process on a plist nested tens of thousands of levels deep. It now reads as{}like any other unusable plist. Plist files over 1 MiB are refused up front. -
Fixed: nameless dependencies from a mangled
Podfile.lock.ios-depsno longer reports an entry namedlibrary::for a truncated or damaged line such as- (2.0).
0.7.x
0.7.13 (October 7 2026)
- The CLI is licensed under MIT. The repository, the crate and every published package — the release archives, the Homebrew formula and all npm packages — now carry the license.
- Fixed:
build-env machine-labelon macOS and Windows. Outside CI it printed an empty line, because it called/usr/bin/hostname, which doesn't exist on macOS or Windows. A localxcode post-actiontherefore registered builds without a machine name, andCI=truewithout$HOSTNAMEproduced a bareci. The hostname is now read directly. - Corrected the
@bugsee/clinpm instructions. Run it once asnpx @bugsee/cli; a barenpx bugsee-clioutside a project that installed it fails withE404. See Installation.
0.7.12 (October 7 2026)
Register a build without its artefact, pick up files under a new suffix without
waiting for a release, and Android SYMBOL_TABLE native symbols upload correctly.
-
upload buildcan register a build without shipping its artefact. Omit--artifact: the build is registered with the metadata you pass, and no artefact bytes are uploaded. That is the normal case on every platform that has not turned on size analysis, and the only case a web build can express.--depsand--timingsstill travel without an artefact. The options that only describe how artefact bytes move (--mapping,--chunked,--out) are rejected with exit20rather than silently ignored. See Builds & artefacts. -
debug-files upload --extension <SUFFIX>. Picks up files whose name ends in a spelling the CLI doesn't know yet, for any--type, so a toolchain change no longer needs a CLI release before its symbols upload. Repeat the flag or comma-separate values; the leading.is optional. Suffixes add to each type's built-in names, and the content checks (ELF build-id, PDB container, dSYMDWARFfolder) still apply. See Common options. -
Fixed: Android
SYMBOL_TABLEsymbols uploaded nothing. Withndk.debugSymbolLevel = 'SYMBOL_TABLE'— what the React Native config plugin sets —native-debug-symbols.zipcontains onlylib*.so.symfiles. Every entry was dropped and the upload exited0having sent nothing..so.symfiles are now keyed by their GNU build-id like.so. They carry function names only;file:lineframes still needFULL. -
Fixed:
--forcewas ignored for native uploads.--type elfand Rust ELF uploads dropped it, so switching a library fromSYMBOL_TABLEtoFULL(same build-id) was skipped as already on the server.--forceis now honoured, and when full-debug libraries are skipped that way the run tells you to pass it. -
Fixed: one upload per build-id. Two files for one library — a stripped
.sobeside its split-debug companion — were registered concurrently and the stripped one could win. Now only one is uploaded, preferring DWARF, then a symbol table, then the larger file.
0.7.11 (September 18 2026)
Source-map tooling for real-world web builds: protection against breaking Subresource Integrity, ways to leave third-party code alone, and an option to keep your original source off the wire.
-
sourcemaps injectrefuses a build that pins its own script hashes. Injecting appends bytes to every.jsfile, so a hash your HTML already carries (Subresource Integrity) stops matching and the browser refuses to run the script: the page loads and nothing executes.injectnow detectsintegrityon<script>and onmodulepreload/preloadlinks in the HTML under the paths you give it and exits20instead. It only refuses over files it would actually rewrite, so re-runninginjecton an already-stamped build stays a no-op. Pass--allow-srifor a build that recomputes its hashes afterwards. It cannot see integrity that never reaches the emitted HTML (a manifest read by a server template, a page rendered at request time). See Source maps. -
sourcemaps inject --exclude <glob>(repeatable) leaves part of a build output alone — for example--exclude '**/node_modules/**'to keepinjectout of vendored third-party code inside the build directory. The pattern is matched against the absolute path, the path relative to the current directory and the path relative to each root, so it works whichever way you pass the directory. An empty or unparseable pattern is a configuration error (exit20) rather than a silent "matches nothing". -
debug-files upload --strip-sources-contentuploads each source map without its embedded original source.sourcesContentcarries your code verbatim and is what lets a symbolicated crash show source lines; stripping it keeps file/line/column resolution and drops the snippet. The map on disk is never modified — only the uploaded copy — and indexed maps are stripped too. Only valid with--type sourcemaps. See Source maps. -
Fixed:
--dry-runfailed on an un-keyed map.debug-files upload --type sourcemaps --dry-runexited11on the first map without a debug ID, which made the safe preview unusable on a freshly built directory (sourcemaps inject --dry-runwrites nothing, so every map is still un-keyed). Such a map is now reported and counted, and a real run still exits11.
0.7.10 (September 18 2026)
Source maps upload several at a time, an empty build can be a no-op, and a path you named but the tool can't find now stops the run.
-
Source-map uploads run concurrently.
debug-files upload --type sourcemapssent one map at a time, so a web build with one map per chunk spent most of its upload time waiting on round-trips. Against a server with 50 ms of latency, 60 maps went from 7.1 s to 1.3 s and 200 maps from 23.6 s to 4.1 s.--concurrency <N>(1..=32) sets a ceiling; left unset it scales with the batch — one upload per 8 maps, at least 4, at most 8.--concurrency 1restores the previous sequential behaviour. An explicit--uuidforces sequential uploads whatever the ceiling, because it keys every map under one ID and those registrations must not race. See Source maps. -
--allow-emptymakes "nothing to upload" a success. A monorepo package built without maps, or a framework whose server output has none, previously failed the caller's build with exit10. The flag applies to--type sourcemaps;xcode upload-dsymsalready treated nothing-to-upload as success. -
A path that does not exist is now an error.
debug-files upload --type sourcemaps dist/ missing/used to warn aboutmissing/, upload what it found underdist/, and exit0. It now exits10withpath does not exist: …before uploading anything — a path you named and the tool can't find is a typo or a build that didn't run, and half-uploading a build's symbols hides that until a crash is unsymbolicated. Drop the missing path from the invocation if you relied on the old leniency. The bundler plugins pass a single output directory and are unaffected. -
A failed upload stops the batch. Now that uploads are concurrent, the first failure cancels the rest instead of letting every remaining map pack, register and transfer into a server that has already refused one — a rejected token on a 200-chunk build was 400 doomed round-trips.
-
--concurrencyand--allow-emptyare rejected for other--types (exit20) rather than accepted and ignored, so a caller who passed--allow-emptyto keep a build green can't still get exit10. -
A throttled request is retried. The symbol-metadata and build-registration
POSTs are sent without status retries, because a 5xx may mean the server processed the request and only the response was lost. A429carries no such ambiguity — the request was rejected without being processed — so it is now retried with the usual backoff. Those requests are also the first thing a server throttles when several uploads run at once.
0.7.9 (September 17 2026)
sourcemaps injectregisters a debug ID another tool already wrote. A bundle carrying its own//# debugId=— Rollup 4 writes one withoutput.sourcemapDebugIds— counted as already injected, so it never got theglobalThis._bugseeDebugIdsruntime registration and the SDK could not attach its debug ID to a crash frame. Such a bundle now keeps its ID, since its map already carries it, and gains only the registration, without a second comment. It is still never re-keyed. See Source maps.
0.7.8 (September 17 2026)
Re-uploading a symbol the server already has no longer fails, including in an Xcode build phase, and source-map uploads handle stylesheet maps and rebuilt bundles correctly.
-
Symbols the server already has are skipped instead of failing. A re-upload of a symbol already on the server failed with exit code
30. It is now reported as already existing and exits0, as the exit-code contract describes. When you uploaded a directory, that failure also stopped the run, so the files that had changed were never uploaded — for example, on the second production build of a web app with one unchanged chunk. This applies to dSYM, PDB, Rust, IL2CPP line map, source map, ProGuard and ELF uploads. -
xcode upload-dsymsno longer fails your Xcode build on rebuilds. A rebuild whose dSYMs were unchanged failed the build because of the issue above. Similarly,xcode post-actionnow reportsdsym_uploaded: truein its JSON result when every dSYM was already on the server, instead offalse. -
Stylesheet and type-declaration source maps are skipped. When
debug-files upload --type sourcemapsscans a directory, maps named.css.map,.d.ts.map,.d.mts.mapor.d.cts.mapare skipped without being read. Previously they made the whole upload fail with exit code11. Any other map without a debug ID still fails the run, but now before anything is uploaded, so it no longer leaves a partial upload behind (a network or server error part-way through still can). A map you name explicitly on the command line, and a scan that leaves nothing to upload, also still fail. -
sourcemaps injectderives a bundle's debug ID from its source map as well as the bundle itself. The server keeps one source map per debug ID, and a minifier often produces identical JavaScript for a source edit that only moves lines — so an ID based on the bundle alone kept the stale map on the server. This includes webpack 5 rebuilds with[contenthash]filenames, which leave the already-injected bundle on disk and write only a new map:injectnow gives such a bundle a new ID when its map comes back without a debug ID and with different content. A//# debugId=comment written by another tool is never changed, and a bundle without a source map keeps its bundle-only ID. When a newly injected bundle sits beside a map that already carries a different ID, the map is updated to the bundle's ID with a warning. See Source maps.noteAfter you upgrade, every bundle that has a source map gets a new debug ID once, so its map is uploaded again. Nothing else needs to change.
-
--forcenow applies to source maps.debug-files upload --type sourcemaps --forceasks the server to replace a map it already has, as--forcealready did for dSYM, PDB, Rust and IL2CPP line map uploads.
0.7.7 (September 16 2026)
Adds a dSYM upload command for Xcode build phases and prebuilt binaries for Windows on ARM64.
-
New command:
xcode upload-dsyms. Uploads dSYMs from an Xcode Run Script build phase. It does not register a build or upload build info, and none of theBUGSEE_BUILD_INFO_*gating applies, so it is safe to run on every build. It is designed for setups such as React Native and Flutter, where a config plugin can add a build phase rather than a scheme post-action.- A real failure fails the build: a missing, empty or rejected app token
(
20/21), a server or network error (30/31), or a dSYM folder or bundle that could not be read (10/11). Finding nothing to upload — no dSYM folder, or no.dSYMbundles in it — is a success; finding bundles and uploading none is not. - Failing the build and running in the background are controlled
independently:
--fail/--no-fail(environment variableBUGSEE_DSYM_UPLOAD_NO_FAIL) and--background/--no-background(BUGSEE_DSYM_UPLOAD_BACKGROUND). A flag overrides its environment variable. - The default is to fail on error and upload synchronously.
--no-failon its own also moves the upload to the background unless you pass--no-background.--no-fail --no-backgroundis usually what CI wants: the build never breaks, but it still waits, so a runner tearing down its processes cannot kill the upload mid-flight. - Asking to fail the build and run in the background is refused, because a
background process's exit code reaches nobody: exit
2when set with flags,20when set through environment variables. xcode post-actionis unchanged:BUGSEE_BUILD_INFO_ENABLED=0still disables its dSYM upload.
- A real failure fails the build: a missing, empty or rejected app token
(
-
Windows on ARM64. Prebuilt binaries are now published for
aarch64-pc-windows-msvc, with the matching npm package@bugsee/cli-win32-arm64. The PowerShell install script detects ARM64 hosts, including when run from 32-bit PowerShell, andbugsee-cli updateworks on Windows ARM64. -
Fixes a crash caused by a non-UTF-8 environment variable.
vcs-metadata,build-env machine-labelandxcode post-actioncrashed with exit code101— outside the documented exit codes — when any variable in the environment had a name or value that was not valid UTF-8, even one the CLI does not read.
0.7.6 (September 16 2026)
A packaging and security release. Commands, exit codes, JSON output and the upload format are unchanged from 0.7.5.
-
New npm package:
@bugsee/cli. The binary ships in per-platform packages (@bugsee/cli-darwin-arm64,@bugsee/cli-darwin-x64,@bugsee/cli-linux-arm64,@bugsee/cli-linux-x64,@bugsee/cli-win32-x64), declared as optional dependencies pinned to the same version. npm installs only the one for your platform, and nothing runs at install time, so it works with--ignore-scripts. If no platform package resolves, apostinstallstep downloads the binary from the GitHub release instead, and never fails the install.@bugsee/bugsee-cliis unchanged and keeps working. A Windows ARM64 package followed in 0.7.7. -
Releases now reach
download.bugsee.comautomatically. The install scripts andbugsee-cli updatedownload from there. 0.7.4 had not been published there, so neither offered that version. -
Security: updates the TLS library used for every upload (
rustls0.23.45) to fix RUSTSEC-2026-0285, where TLS 1.3 handshake messages were accepted across encryption level boundaries. -
Fixes a crash in
build-env read-pliston binary plist files containing out-of-range dates.
0.7.5 (August 24 2026)
A security maintenance release with no functional changes: commands, exit codes, JSON output and the upload format are identical to 0.7.4.
-
Security: fixes two denial-of-service issues in XML parsing, RUSTSEC-2026-0194 and RUSTSEC-2026-0195, which were reachable when
build-env read-plistparses an XMLInfo.plist. -
Other dependency updates, with no change in behaviour.
0.7.4 (August 11 2026)
Adds symbol uploads for Unity IL2CPP line-number mappings and for Rust projects.
-
New upload type:
debug-files upload --type il2cpp-linemap. Uploads Unity IL2CPPLineNumberMappings.jsontogether with its siblingMethodMap.tsvandil2cppFileRoot.txt, keyed by thelibil2cpp/UnityFrameworkmodule UUIDs. Pass the UUIDs with--uuid(repeat it or comma-separate the values), or append more with--il2cpp-uuid. -
New upload type:
debug-files upload --type rust. One command for a Cargo project, whatever target it built for. It finds whichever debug format is present — a.dSYM(Apple), a.pdb(Windows MSVC targets), or the ELF binary itself, keyed by its GNU build ID (Linux and Android) — and uploads each one.- Formats are detected from file contents rather than the host OS, so a
cross-compiled
target/<triple>/releaseuploads correctly from any machine. - Cargo intermediates (
deps/,build/,incremental/,.fingerprint/) are skipped. --uuidis rejected: every Rust debug format carries its own identity.
- Formats are detected from file contents rather than the host OS, so a
cross-compiled
-
Build-configuration checks for Rust. Some Cargo settings produce an upload that is accepted but then symbolicates nothing: no debug info (
debug = 0), no.dSYM(split-debuginfonot set to"packed"), or no build ID (missing-Wl,--build-id). The CLI warns about these with the exact setting that fixes them, and when it finds nothing uploadable it fails with the full recipe instead of a bare "not found".
See Uploading debug information files.
0.7.3 (July 16 2026)
A security fix for credentials appearing in logs, plus hardening of update and
sourcemaps inject.
-
Security: the app token and upload signatures no longer appear in logs. A network error message included the full request URL at the default log level, exposing the app token and the signature of the storage upload URL — including in the
xcode post-actionbackground log file. URLs are now removed from error messages and redacted in debug logs. -
bugsee-cli updateis stricter about what it downloads. It refuses a non-HTTPS download base (BUGSEE_CLI_UPDATE_BASE_URL) other than a loopback address, limits download sizes (512 MiB for the release archive, 1 MiB for metadata) before the SHA-256 check, and rejects archives containing absolute or..paths. See Updating the CLI. -
sourcemaps injectonly follows a//# sourceMappingURL=inside the bundle's directory. A URL with a..component or an absolute path is no longer followed; a<bundle>.mapfile next to the bundle is still used. -
Fixes a possible file collision during native symbol uploads when two libraries uploaded at the same time share a build ID.
0.7.2 (July 11 2026)
- A maintenance update to the library the CLI uses to read debug files. The identifiers it reads from ELF and Mach-O files are unchanged.
0.7.1 (July 6 2026)
- A maintenance update to the library the CLI uses to read debug files. The identifiers it reads from ELF and Mach-O files are unchanged.
0.7.0 (June 25 2026)
Adds self-hosted install scripts and uploads native symbols per library.
-
New install scripts on
download.bugsee.com.install.sh(macOS and Linux) andinstall.ps1(Windows PowerShell) resolve the latest version, download the binary for your host fromdownload.bugsee.comwith no GitHub dependency, verify its SHA-256 checksum, and install it. Override the version, install directory and download location withBUGSEE_CLI_VERSION,BUGSEE_CLI_INSTALL_DIRandBUGSEE_CLI_BASE_URL. See Installation. -
Native symbols are uploaded per library.
debug-files upload --type elfnow uploads each.soas its own symbol, keyed by the library's GNU build ID instead of the build-level--uuid. Native symbols therefore no longer collide with the ProGuard mapping for the same build, and an unchanged library is skipped before its bytes are transferred. A.sobuilt without-Wl,--build-idcannot be matched at crash time, so it is skipped with a warning. See Native (ELF). -
Fixes
upload build-infofailing with HTTP 403. The storage upload was rejected withSignatureDoesNotMatch. Seeupload build-info.