{"record":{"id":"7a51b94e98b8ea19","repo":"crowdsecurity/crowdsec","slug":"getting-lp-usage-metrics-by-origin-s-w","errorCode":null,"errorMessage":"getting LP usage metrics by origin %s: %w","messagePattern":"getting LP usage metrics by origin (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/database/metrics.go","lineNumber":42,"sourceCode":"\n\treturn metric, nil\n}\n\nfunc (c *Client) GetLPUsageMetricsByMachineID(ctx context.Context, machineId string, toSend bool) ([]*ent.Metric, error) {\n\tquery := c.Ent.Metric.Query().\n\t\tWhere(\n\t\t\tmetric.GeneratedTypeEQ(metric.GeneratedTypeLP),\n\t\t\tmetric.GeneratedByEQ(machineId),\n\t\t)\n\n\tif toSend {\n\t\tquery = query.Where(metric.PushedAtIsNil())\n\t}\n\n\tmetrics, err := query.All(ctx)\n\tif err != nil {\n\t\tc.Log.Warningf(\"GetLPUsageMetricsByOrigin: %s\", err)\n\t\treturn nil, fmt.Errorf(\"getting LP usage metrics by origin %s: %w\", machineId, err)\n\t}\n\n\treturn metrics, nil\n}\n\nfunc (c *Client) GetBouncerUsageMetricsByName(ctx context.Context, bouncerName string) ([]*ent.Metric, error) {\n\tmetrics, err := c.Ent.Metric.Query().\n\t\tWhere(\n\t\t\tmetric.GeneratedTypeEQ(metric.GeneratedTypeRC),\n\t\t\tmetric.GeneratedByEQ(bouncerName),\n\t\t\tmetric.PushedAtIsNil(),\n\t\t).\n\t\tAll(ctx)\n\tif err != nil {\n\t\tc.Log.Warningf(\"GetBouncerUsageMetricsByName: %s\", err)\n\t\treturn nil, fmt.Errorf(\"getting bouncer usage metrics by name %s: %w\", bouncerName, err)\n\t}\n","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/database/metrics.go#L24-L60","documentation":"GetLPUsageMetricsByMachineID queries metric rows filtered by machine ID (and optionally not-yet-pushed). It returns this wrapped error when the SELECT itself fails — not when there are simply no rows (that returns an empty slice). The %w carries the raw database error for errors.Is/As inspection.","triggerScenarios":"Calling GetLPUsageMetricsByMachineID when the ent query fails: DB file unreadable/locked, disk I/O error, corrupted SQLite DB, or context canceled mid-query.","commonSituations":"CrowdSec data directory owned by the wrong user (permission denied on the sqlite file); NFS-mounted DB with locking problems; DB corrupted after an unclean shutdown.","solutions":["Check the Warningf log 'GetLPUsageMetricsByOrigin: <err>' for the underlying query error.","Fix filesystem permissions on the DB file so the crowdsec user can read it.","Verify DB integrity with sqlite3 'PRAGMA integrity_check' and restore from backup if corrupted.","Retry on transient errors; an empty result is returned as an empty slice with nil error, so no special handling is needed for 'no rows'."],"exampleFix":"// caller guard before/after\n// before: assuming error means 'no rows'\nmetrics, err := client.GetLPUsageMetricsByMachineID(ctx, machineID, true)\nif err != nil { return err } // it's a real query failure, not empty data\n// after: distinguish empty vs failure\nif err != nil { return fmt.Errorf(\"usage metrics query: %w\", err) }\nif len(metrics) == 0 { /* nothing to push */ }","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"metrics, err := client.GetLPUsageMetricsByMachineID(ctx, machineID, true)\nif err != nil {\n    return fmt.Errorf(\"querying LP usage metrics: %w\", err) // real query failure\n}\n// len(metrics)==0 means no rows, not an error","preventionTips":["Treat empty slices as normal; only errors indicate DB problems.","Check DB file permissions after any restore/copy of /var/lib/crowdsec.","Avoid placing the SQLite DB on NFS.","Verify the error chain with errors.Is — the %w carries the raw driver error."],"tags":["database","sqlite","query-failure","metrics"],"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-14T05:17:10.506Z"}