crowdsecurity/crowdsec · error

gzip reader for initial bundle: %w

Error message

gzip reader for initial bundle: %w

What it means

The challenge runtime decompresses the embedded initial_bundle.js.gz at startup via a sync.Once; gzip.NewReader fails when the baked-in blob is not valid gzip data. The error is cached in initialBundleErr so every subsequent request for the bundle fails the same way. This almost always means the build artifact is broken, not runtime data.

Source

Thrown at pkg/appsec/challenge/static_bundle.go:58

// challenge package alongside PowWorkerJS.
var FPScannerJS = challengejs.FPScannerJS

// seedCacheFromInitialBundle decompresses the build-time obfuscated challenge
// code (initial_bundle.js.gz) and stores it as the static code served on every
// challenge page. Cheap (~ms) — eliminates the obfuscation that startup would
// otherwise pay.
func (c *ChallengeRuntime) seedCacheFromInitialBundle() error {
	initialBundleOnce.Do(func() {
		decompressStart := time.Now()

		if len(initialBundleGz) == 0 {
			initialBundleErr = errors.New("baked-in initial_bundle.js.gz is empty (was `go generate` run?)")
			return
		}

		gz, err := gzip.NewReader(bytes.NewReader(initialBundleGz))
		if err != nil {
			initialBundleErr = fmt.Errorf("gzip reader for initial bundle: %w", err)
			return
		}
		defer gz.Close()

		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")
	})

View on GitHub (pinned to 909b515798)

Solutions

  1. Run `go generate` (or `make build`) to (re)create initial_bundle.js.gz, then rebuild the binary
  2. Inspect the embedded .gz file: `file initial_bundle.js.gz` and `gzip -t` to confirm it is valid gzip
  3. Clean the build cache / rebuild from a fresh checkout so a stale blob is not embedded
  4. Verify the build pipeline runs the generate step before `go build`

Example fix

// before: build without generated asset
go build ./cmd/crowdsec
// after
go generate ./pkg/appsec/challenge/...
go build ./cmd/crowdsec
Defensive patterns

Strategy: try-catch

Validate before calling

if len(initialBundleGz) == 0 || !bytes.HasPrefix(initialBundleGz, []byte{0x1f, 0x8b}) {
    return errors.New("embedded initial bundle missing or not gzip")
}

Try / catch

bundle, err := challenge.InitialBundle()
if err != nil {
    log.Error().Err(err).Msg("embedded bundle broken; run go generate and rebuild")
    http.Error(w, "bundle unavailable", http.StatusInternalServerError)
    return
}

Prevention

When it happens

Trigger: The embedded initialBundleGz contains corrupt, empty-after-check, or non-gzip bytes — e.g. go:generate never ran, or a bad build pipeline stored a placeholder/HTML error page in the .gz file.

Common situations: Fresh checkout where `go generate ./pkg/appsec/challenge/...` was not run; CI builds the binary without the generate step; a partially-written or LFS-mishandled .gz asset; a stale build cache embedding an old broken blob.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06). Data as JSON: /api/errors/e77329443f920837. Report an issue: GitHub.