crowdsecurity/crowdsec · error
on_challenge_submit hooks are only valid in-band, not under
Error message
on_challenge_submit hooks are only valid in-band, not under outofband
What it means
CrowdSec AppSec component rules support challenge hooks (on_challenge / on_challenge_submit) only for in-band rule collections. The WAF config loader validates the whole WAF config tree and rejects the config if these hook names appear under the `outofband` section, where per-request challenge interaction is meaningless. This is a startup-time configuration validation error: the config fails to load and the AppSec component is not initialized.
Source
Thrown at pkg/appsec/appsec.go:1012
if wc.InBand != nil {
if ret.InBandHooks, err = buildPhaseHooks(ctx, "inband",
wc.InBand.PreEval, wc.InBand.PostEval, wc.InBand.OnMatch, patcher); err != nil {
return nil, err
}
}
if wc.OutOfBand != nil {
if ret.OutOfBandHooks, err = buildPhaseHooks(ctx, "outofband",
wc.OutOfBand.PreEval, wc.OutOfBand.PostEval, wc.OutOfBand.OnMatch, patcher); err != nil {
return nil, err
}
if len(wc.OutOfBand.OnChallenge) > 0 {
return nil, errors.New("on_challenge hooks are only valid in-band, not under outofband")
}
if len(wc.OutOfBand.OnChallengeSubmit) > 0 {
return nil, errors.New("on_challenge_submit hooks are only valid in-band, not under outofband")
}
}
// on_challenge hooks: merge top-level and inband-scoped (both are in-band only).
onChallengeHooks := wc.OnChallenge
if wc.InBand != nil {
onChallengeHooks = append(onChallengeHooks, wc.InBand.OnChallenge...)
}
if ret.CompiledOnChallenge, err = buildHookList(ctx, onChallengeHooks, hookOnChallenge, patcher); err != nil {
return nil, err
}
// Defining any on_challenge hook implies we need the challenge runtime to
// validate cookies and submissions, even if the hook bodies never call
// SendChallenge() themselves.
if len(ret.CompiledOnChallenge) > 0 {
patcher.NeedWASMVM = trueView on GitHub (pinned to 909b515798)
Solutions
- Move the on_challenge / on_challenge_submit hook definitions from the `outofband:` section to the top level or the `inband:` section of the WAF config
- If the hook was added by mistake under outofband and is not needed, delete the entry
- Re-validate the config indentation: YAML nesting mistakes are the usual root cause
Example fix
// before (WAF config yaml)
outofband:
on_challenge_submit:
- console_log()
// after
inband:
on_challenge_submit:
- console_log() Defensive patterns
Strategy: validation
Validate before calling
if cfg.OutOfBand != nil && (len(cfg.OutOfBand.OnChallenge) > 0 || len(cfg.OutOfBand.OnChallengeSubmit) > 0) {
return fmt.Errorf("on_challenge/on_challenge_submit must be defined in-band, not under outofband")
} Prevention
- Keep challenge hooks only in in-band collections; lint WAF configs for hook placement before deployment
- cscli hubtool or a CI check can grep configs for `outofband:` blocks containing on_challenge
When it happens
Trigger: Loading a WAF AppSec config YAML where `outofband: on_challenge_submit: [...]` (or `outofband: on_challenge: [...]`) is defined; each hook list under OutOfBand is checked in the WAF-config compilation path in pkg/appsec/appsec.go.
Common situations: A user copies an in-band challenge example into their out-of-band collection; a config file merges snippets where challenge hooks ended up at the wrong YAML nesting level; upgrading an older config that predated the in-band/out-of-band hook split.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- ref cannot be empty
- max_body_size must be a positive integer
- invalid challenge master_secret: %w
- appsec rule name is empty for %s
- path must start with /
AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06).
Data as JSON: /api/errors/7a1feddbde62728a.
Report an issue: GitHub.