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 the

View on GitHub (pinned to 909b515798)

Solutions

  1. Regenerate the bundle: run the initialbundle generator (`go generate ./pkg/appsec/challenge/...`) and verify initial_bundle.js.gz decompresses to non-empty JavaScript.
  2. Inspect the generator pipeline (compile -> obfuscate -> gzip) for a step silently producing empty output.
  3. Validate the source JS files exist and are non-empty before generation.
  4. 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

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


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