{"record":{"id":"71a25bd528a94b6e","repo":"crowdsecurity/crowdsec","slug":"query-decision-failed-w","errorCode":null,"errorMessage":"query decision failed: %w","messagePattern":"query decision failed: %w","errorType":"exception","errorClass":"QueryFail","httpStatus":null,"severity":"error","filePath":"pkg/database/decisions.go","lineNumber":152,"sourceCode":"\tquery, err = applyDecisionFilter(query, filter)\n\tif err != nil {\n\t\treturn []*ent.Decision{}, err\n\t}\n\n\terr = query.Select(\n\t\tdecision.FieldID,\n\t\tdecision.FieldUntil,\n\t\tdecision.FieldScenario,\n\t\tdecision.FieldType,\n\t\tdecision.FieldStartIP,\n\t\tdecision.FieldEndIP,\n\t\tdecision.FieldValue,\n\t\tdecision.FieldScope,\n\t\tdecision.FieldOrigin,\n\t).Scan(ctx, &data)\n\tif err != nil {\n\t\tc.Log.Warningf(\"QueryDecisionWithFilter : %s\", err)\n\t\treturn []*ent.Decision{}, fmt.Errorf(\"query decision failed: %w\", QueryFail)\n\t}\n\n\treturn data, nil\n}\n\n// ent translation of https://stackoverflow.com/a/28090544\nfunc longestDecisionForScopeTypeValue(s *sql.Selector) {\n\tt := sql.Table(decision.Table)\n\ts.LeftJoin(t).OnP(sql.And(\n\t\tsql.ColumnsEQ(\n\t\t\tt.C(decision.FieldValue),\n\t\t\ts.C(decision.FieldValue),\n\t\t),\n\t\tsql.ColumnsEQ(\n\t\t\tt.C(decision.FieldType),\n\t\t\ts.C(decision.FieldType),\n\t\t),\n\t\tsql.ColumnsEQ(","sourceCodeStart":134,"sourceCodeEnd":170,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/database/decisions.go#L134-L170","documentation":"Returned by Client.QueryDecisionWithFilter when its Scan of distinct decision fields (value/scope/origin) fails. Wraps QueryFail (\"unable to query\"); the real error is logged as 'QueryDecisionWithFilter : <err>'. Called by GetDecision (the LAPI GET /decisions handler).","triggerScenarios":"DB failure during the distinct-columns scan: connection error, SQLite lock, schema mismatch, or a filter applied via applyDecisionFilter already returned OK but the raw scan hits driver issues. Caller GetDecision propagates it to LAPI clients as an HTTP 500/403 response.","commonSituations":"Bouncer polls /decisions during a MySQL restart or SQLite backup lock; upgraded LAPI serving decisions to an old schema DB; disk I/O errors on the DB host.","solutions":["Check LAPI logs for 'QueryDecisionWithFilter :' to see the driver error","Verify DB reachability/locks; pause backups that take SQLite exclusive locks or use a server DB","After a CrowdSec upgrade, ensure migrations completed (`cscli db migrate`)","Have bouncers retry with backoff — this is a transient availability error, not a bad request"],"exampleFix":"// bouncer-side retry (pseudo)\n// before\nresp = lapi.GetDecisions()\n// after\nfor i := 0; i < 3; i++ {\n    resp, err = lapi.GetDecisions()\n    if err == nil { break }\n    time.Sleep(backoff(i))\n}","handlingStrategy":"retry","validationCode":"// client-side: confirm LAPI health before relying on decisions\nresp, err := http.Get(lapiURL + \"/health\")\nif err != nil || resp.StatusCode != 200 { return errors.New(\"LAPI unhealthy, skip decisions poll\") }","typeGuard":null,"tryCatchPattern":"decisions, err := client.GetDecision(ctx, filter)\nif err != nil {\n    // QueryFail sentinel: transient DB issue; back off and retry\n    time.Sleep(2 * time.Second)\n    decisions, err = client.GetDecision(ctx, filter)\n}","preventionTips":["Implement backoff+retry in bouncers — DB hiccups are expected","Stop SQLite backups from taking exclusive locks during bouncer polls","Keep LAPI and DB on the same healthy host/network or use a server DB"],"tags":["database","crowdsec","lapi","decisions"],"backgroundTag":"database-query-failed","analyzedSha":"909b5157986a2b2c2163300fdaef5ed01289f7d2","analyzedAt":"2026-09-06T12:27:26.012Z","contentChangedAt":"2026-09-06T12:27:26.012Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}