crowdsecurity/crowdsec · error
warm dynamic key module: %w
Error message
warm dynamic key module: %w
What it means
After seeding the initial bundle, NewChallengeRuntime pre-warms the current epoch's dynamic key module so the first GetChallengePage does not pay the ~5s obfuscation cost on the request path. If currentDynamicModule fails during this synchronous pre-warm, the constructor fails with this wrapped error.
Source
Thrown at pkg/appsec/challenge/challenge.go:500
// Load the build-time-obfuscated challenge code from the baked-in bundle so
// we can serve immediately.
if err := challengeRuntime.seedCacheFromInitialBundle(); err != nil {
// Initial bundle missing/corrupt (e.g. `go generate` not run): fall back
// to obfuscating the challenge code synchronously.
logger.Warnf("failed to load baked-in initial challenge bundle (%v); falling back to synchronous generation", err)
if err := challengeRuntime.generateAndCacheChallengeJS(ctx); err != nil {
return nil, fmt.Errorf("failed to generate initial challenge bundle: %w", err)
}
}
// Pre-warm the current epoch's dynamic module so the first GetChallengePage
// doesn't pay the ~5s obfuscation cost on the request path. The background
// pre-warmer then re-obfuscates on every rotation; it runs under a context
// owned by Close() rather than the constructor ctx, so a reload (which
// reuses the process ctx) can stop it — see Close().
if !resolvedOpts.skipPreWarm {
if _, err := challengeRuntime.currentDynamicModule(ctx); err != nil {
return nil, fmt.Errorf("warm dynamic key module: %w", err)
}
runCtx, cancel := context.WithCancel(ctx)
challengeRuntime.preWarmCancel = cancel
go challengeRuntime.dynamicModulePreWarmer(runCtx)
}
logger.WithFields(log.Fields{
"rotation_interval": rotationInterval,
"cookie_ttl": cookieTTL,
"max_cookie_len": maxCookieLen,
"pow_difficulty": defaultPowDifficulty,
"crypto_pool_size": cryptoPoolSize,
}).Info("WAF challenge runtime initialized")
return challengeRuntime, nil
}
View on GitHub (pinned to 909b515798)
Solutions
- Increase the deadline on the context passed to NewChallengeRuntime to allow the ~5s obfuscation to complete.
- Check the wrapped error for the true cause (obfuscation failure, ctx cancellation) and address it.
- Set skipPreWarm in options only if you accept paying the cost on the first request — not a real fix, but unblocks startup.
- Verify system resources (CPU/memory) on the host running CrowdSec AppSec.
Example fix
// before ctx, cancel := context.WithTimeout(parentCtx, time.Second) // after: allow for the ~5s obfuscation cost ctx, cancel := context.WithTimeout(parentCtx, 30*time.Second)
Defensive patterns
Strategy: fallback
Try / catch
rt, err := NewChallengeRuntime(ctx, opts)
if err != nil && strings.Contains(err.Error(), "warm dynamic key module") {
logger.WithError(err).Error("pre-warm failed; first requests will be slow or fail")
} Prevention
- Do not wrap the constructor context in a tight timeout.
- Keep skipPreWarm=false in production.
- Monitor pre-warm failures in logs after upgrades.
- Ensure hosts have enough CPU/memory for the obfuscator.
When it happens
Trigger: Calling NewChallengeRuntime with skipPreWarm=false when the synchronous call to currentDynamicModule(ctx) errors — typically because the underlying obfuscation/generation step failed or ctx was cancelled during pre-warm.
Common situations: Startup contexts with too-short deadlines; hosts too slow/memory-constrained to run the JS obfuscator within the deadline; an already-cancelled context from a reload path.
Related errors
- failed to generate initial challenge bundle: %w
- appsec datasource requires a hub. this is a bug, please repo
- appsec datasource requires a lapi client configuration. this
- ErrChallengeFields
- ErrChallengeTicket
AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06).
Data as JSON: /api/errors/24b596440828c665.
Report an issue: GitHub.