{"record":{"id":"cd8098329ae27973","repo":"crowdsecurity/crowdsec","slug":"initial-bundle-is-empty-after-decompression","errorCode":null,"errorMessage":"initial bundle is empty after decompression","messagePattern":"initial bundle is empty after decompression","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/appsec/challenge/static_bundle.go","lineNumber":81,"sourceCode":"\t\tdecoded, err := io.ReadAll(gz)\n\t\tif err != nil {\n\t\t\tinitialBundleErr = fmt.Errorf(\"decompress initial bundle: %w\", err)\n\t\t\treturn\n\t\t}\n\t\tinitialBundle = string(decoded)\n\n\t\tc.log().WithFields(log.Fields{\n\t\t\t\"compressed_bytes\":   len(initialBundleGz),\n\t\t\t\"decompressed_bytes\": len(initialBundle),\n\t\t\t\"duration_ms\":        time.Since(decompressStart).Milliseconds(),\n\t\t}).Debug(\"decompressed baked-in obfuscated challenge code\")\n\t})\n\n\tif initialBundleErr != nil {\n\t\treturn initialBundleErr\n\t}\n\tif initialBundle == \"\" {\n\t\treturn errors.New(\"initial bundle is empty after decompression\")\n\t}\n\n\tc.challengeCode = initialBundle\n\n\treturn nil\n}\n\n// buildChallengeBundle substitutes the internal-path placeholders into the\n// (minified, not-yet-obfuscated) challenge code. Used only by the synchronous\n// fallback below — the normal path serves the pre-obfuscated initial bundle.\nfunc (*ChallengeRuntime) buildChallengeBundle() string {\n\treturn strings.NewReplacer(\n\t\t\"__CROWDSEC_SUBMIT_PATH__\", ChallengeSubmitPath,\n\t\t\"__CROWDSEC_POW_WORKER_PATH__\", ChallengePowWorkerPath,\n\t).Replace(challengejs.ChallengeCode)\n}\n\n// generateAndCacheChallengeJS is the synchronous fallback used when the","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/appsec/challenge/static_bundle.go#L63-L99","documentation":"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.","triggerScenarios":"NewChallengeRuntime -> seedCacheFromInitialBundle: gzip decompression of initialBundleGz succeeds but yields an empty string (static_bundle.go:81); asserted in TestSeedCacheFromInitialBundle and TestSplitBundle_HookSentinelInBakedBundle.","commonSituations":"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.","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."],"exampleFix":"# before: committed artifact is empty\ngzcat initial_bundle.js.gz   # outputs nothing\n# after: regenerate and verify\ngo generate ./pkg/appsec/challenge/...\ntest -s initial_bundle.js.gz && gzcat initial_bundle.js.gz | head -1","handlingStrategy":"validation","validationCode":"// post-generate smoke test in build scripts\ntest -s pkg/appsec/challenge/initial_bundle.js.gz || exit 1","typeGuard":null,"tryCatchPattern":"if err := seedCacheFromInitialBundle(); err != nil {\n    log.Fatalf(\"challenge assets broken, regenerate with `go generate`: %v\", err)\n}","preventionTips":["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"],"tags":["appsec","build","javascript","embed"],"backgroundTag":"empty-response-body","analyzedSha":"909b5157986a2b2c2163300fdaef5ed01289f7d2","analyzedAt":"2026-09-06T12:27:26.012Z","contentChangedAt":"2026-09-06T12:27:26.012Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}