crowdsecurity/crowdsec · error
unable to run appsec %s filter %s : %w
Error message
unable to run appsec %s filter %s : %w
What it means
During hook evaluation (on_request/on_response/pre_eval), each rule's FilterExpr is an expr-lang expression executed with exprhelpers.Run. If evaluation itself errors (bad syntax compiled past validation, missing variable, wrong return type at runtime), this error wraps it and aborts the hook stage for the request.
Source
Thrown at pkg/appsec/appsec.go:1081
//
// state, when non-nil, is consulted between rule iterations: if
// state.HooksHalted is true (set by a terminal expr helper such as
// RejectSubmission or the on_challenge_submit GrantChallengeCookie),
// remaining rules in this phase are skipped. ProcessOnLoadRules passes
// nil — it has no request state at all.
func (w *AppsecRuntimeConfig) processHooks(hooks []Hook, env map[string]interface{}, hookType string, state *AppsecRequestState) error {
has_match := false
for _, rule := range hooks {
if state != nil && state.HooksHalted {
w.Logger.Debugf("hooks halted by a terminal action; skipping remaining %s rules", hookType)
break
}
if rule.FilterExpr != nil {
output, err := exprhelpers.Run(rule.FilterExpr, env, w.Logger, w.Logger.Level >= log.DebugLevel)
if err != nil {
return fmt.Errorf("unable to run appsec %s filter %s : %w", hookType, rule.Filter, err)
}
switch t := output.(type) {
case bool:
if !t {
w.Logger.Debugf("filter didnt match")
continue
}
default:
w.Logger.Errorf("Filter must return a boolean, can't filter")
continue
}
has_match = true
}
for _, applyExpr := range rule.ApplyExpr {
o, err := exprhelpers.Run(applyExpr, env, w.Logger, w.Logger.Level >= log.DebugLevel)View on GitHub (pinned to 909b515798)
Solutions
- Fix the filter expression in the hook config, verifying every variable against the appsec eval env (request, tx, etc.)
- Test the expression with `cscli explain` or a small expr snippet before deploying
- Check the wrapped error for the exact variable/operation that failed
- Upgrade crowdsec if the expression uses a newer env field added in a recent version
Example fix
// before (hook filter)
filter: request.url.contains('
// after
filter: request.url.Path != '' && request.Method == 'POST' Defensive patterns
Strategy: try-catch
Validate before calling
expr, err := expr.Compile(filter, expr.Env(appsecEnv{}))
if err != nil {
return fmt.Errorf("bad hook filter %q: %w", filter, err)
} Try / catch
if _, err := exprhelpers.Run(rule.FilterExpr, env, logger, false); err != nil {
logger.Errorf("hook filter %q failed: %v — fix or remove the rule", rule.Filter, err)
return // or fall back to default allow/deny policy
} Prevention
- Compile-time validate hook filters with expr.Env at config load
- Reference only documented appsec env variables in filters
- Re-validate custom filters after every crowdsec upgrade
When it happens
Trigger: Rule filter referencing an undefined variable/function in the env; expression raising a runtime error (e.g. type error on nil map access); malformed expression that passed compile but fails at eval.
Common situations: Custom hook expression using a typo'd variable name; expression written against an older appsec env schema after an upgrade; filter accessing request fields absent for that request type.
Related errors
- no zones defined
- no match type defined
- no match value defined
- leaf rule must have zones
- rule has no zones, 'and', or 'or' children
AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06).
Data as JSON: /api/errors/977b902de0048d8a.
Report an issue: GitHub.