crowdsecurity/crowdsec · error
initial bundle is empty after decompression
Error message
initial bundle is empty after decompression
What it means
After decompressing the baked-in initial bundle, seedCacheFromInitialBundle verifies the decompressed text is non-empty before assigning it to challengeCode. An empty decompressed bundle means the generated artifact contained no usable JavaScript, so seeding the challenge cache would silently produce broken challenges; the error surfaces this instead.
Source
Thrown at pkg/appsec/challenge/static_bundle.go:81
decoded, err := io.ReadAll(gz)
if err != nil {
initialBundleErr = fmt.Errorf("decompress initial bundle: %w", err)
return
}
initialBundle = string(decoded)
c.log().WithFields(log.Fields{
"compressed_bytes": len(initialBundleGz),
"decompressed_bytes": len(initialBundle),
"duration_ms": time.Since(decompressStart).Milliseconds(),
}).Debug("decompressed baked-in obfuscated challenge code")
})
if initialBundleErr != nil {
return initialBundleErr
}
if initialBundle == "" {
return errors.New("initial bundle is empty after decompression")
}
c.challengeCode = initialBundle
return nil
}
// buildChallengeBundle substitutes the internal-path placeholders into the
// (minified, not-yet-obfuscated) challenge code. Used only by the synchronous
// fallback below — the normal path serves the pre-obfuscated initial bundle.
func (*ChallengeRuntime) buildChallengeBundle() string {
return strings.NewReplacer(
"__CROWDSEC_SUBMIT_PATH__", ChallengeSubmitPath,
"__CROWDSEC_POW_WORKER_PATH__", ChallengePowWorkerPath,
).Replace(challengejs.ChallengeCode)
}
// generateAndCacheChallengeJS is the synchronous fallback used when theView on GitHub (pinned to 909b515798)
Solutions
- Regenerate the bundle: run the initialbundle generator (`go generate ./pkg/appsec/challenge/...`) and verify initial_bundle.js.gz decompresses to non-empty JavaScript.
- Inspect the generator pipeline (compile -> obfuscate -> gzip) for a step silently producing empty output.
- Validate the source JS files exist and are non-empty before generation.
- Diff/regenerate the committed artifact if a stale or corrupted file was embedded.
Example fix
# before: committed artifact is empty gzcat initial_bundle.js.gz # outputs nothing # after: regenerate and verify go generate ./pkg/appsec/challenge/... test -s initial_bundle.js.gz && gzcat initial_bundle.js.gz | head -1
Defensive patterns
Strategy: validation
Validate before calling
// post-generate smoke test in build scripts test -s pkg/appsec/challenge/initial_bundle.js.gz || exit 1
Try / catch
if err := seedCacheFromInitialBundle(); err != nil {
log.Fatalf("challenge assets broken, regenerate with `go generate`: %v", err)
} Prevention
- Check generator inputs are non-empty before running obfuscation
- Detect zero-byte output in the writeGzip step and abort
- Version-pin the obfuscator to avoid silent behavior changes
When it happens
Trigger: NewChallengeRuntime -> seedCacheFromInitialBundle: gzip decompression of initialBundleGz succeeds but yields an empty string (static_bundle.go:81); asserted in TestSeedCacheFromInitialBundle and TestSplitBundle_HookSentinelInBakedBundle.
Common situations: The initialbundle generator ran against empty or whitespace-only source; the obfuscator/gzip pipeline wrote a zero-byte result; the generated file was truncated or corrupted; an older broken artifact got committed and embedded.
Related errors
- baked-in initial_bundle.js.gz is empty (was `go generate` ru
- esbuild returned no output files
- obfuscator produced empty output
- esbuild returned no output files
- appsec datasource requires a hub. this is a bug, please repo
AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06).
Data as JSON: /api/errors/cd8098329ae27973.
Report an issue: GitHub.