crowdsecurity/crowdsec · error
build dynamic key module: %w
Error message
build dynamic key module: %w
What it means
The challenge page combines a static hook-registration snippet with a dynamic key module carrying the per-epoch key K. GetChallengePage fetches the current dynamic module via currentDynamicModule(ctx); failure is wrapped as 'build dynamic key module'. Without it the page cannot deliver the epoch key and the challenge is unbuildable.
Source
Thrown at pkg/appsec/challenge/challenge.go:585
return "", fmt.Errorf("generate PoW salt: %w", err)
}
powMAC := c.computePowMAC(powSalt, r, ts, difficulty)
if c.log().Logger.IsLevelEnabled(log.DebugLevel) {
issEpoch, issKey := c.keys.Current()
c.log().WithFields(log.Fields{
"r": r,
"epoch": issEpoch,
"k_epoch": fmt.Sprintf("%x", issKey),
"difficulty": difficulty,
}).Debug("issued challenge")
}
// The challenge code only carries the hook registration; the dynamic module
// carries the per-epoch K, so K never appears in plain HTML.
dynamicModule, err := c.currentDynamicModule(ctx)
if err != nil {
return "", fmt.Errorf("build dynamic key module: %w", err)
}
var renderedPage strings.Builder
if err := c.htmlTpl.Execute(&renderedPage, map[string]interface{}{
"JSChallenge": challengeCode,
"DynamicModule": dynamicModule,
"FPScannerPath": ChallengeFPScannerPath,
"PowDifficulty": difficulty,
"PowPrefix": powSalt,
"PowMAC": powMAC,
"Timestamp": ts,
"R": r,
}); err != nil {
return "", fmt.Errorf("render challenge page: %w", err)
}
return renderedPage.String(), nil
}View on GitHub (pinned to 909b515798)
Solutions
- Ensure skipPreWarm is false so the module is pre-warmed at startup.
- Increase the request/HTTP server timeout to tolerate a synchronous ~5s obfuscation.
- Check logs for pre-warmer failures — fix the underlying obfuscation/generation error.
- Restart the runtime if the pre-warmer goroutine was stopped without a reload.
Defensive patterns
Strategy: fallback
Try / catch
page, err := rt.GetChallengePage(ctx, opts)
if err != nil {
if strings.Contains(err.Error(), "build dynamic key module") {
logger.WithError(err).Warn("dynamic module unavailable; serving static challenge only")
}
} Prevention
- Keep skipPreWarm=false so modules are warmed at startup.
- Set HTTP server write timeouts above the obfuscation cost.
- Watch pre-warmer logs; a stopped pre-warmer causes sync generation on the request path.
- Size hosts for the ~5s obfuscation under peak load.
When it happens
Trigger: Calling GetChallengePage when the cache is empty (pre-warm not done/failed) and currentDynamicModule must obfuscate synchronously, which then fails (ctx cancelled/deadline exceeded, generation error).
Common situations: First request after startup with skipPreWarm=true and a short request timeout; pre-warmer goroutine stopped (Close called or panic); obfuscation slower than the request deadline under load.
Related errors
- failed to generate challenge JS: %w
- ErrChallengeFields
- ErrChallengeTicket
- unable to get challenge page: %w
- unable to seal allowlist cookie: %w
AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06).
Data as JSON: /api/errors/3173355eec543f1b.
Report an issue: GitHub.