{"record":{"id":"5beb9d4bcc4c7494","repo":"crowdsecurity/crowdsec","slug":"unable-to-update-bouncer-stream-pull-in-database","errorCode":null,"errorMessage":"unable to update bouncer stream pull in database: %w","messagePattern":"unable to update bouncer stream pull in database: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/database/bouncers.go","lineNumber":149,"sourceCode":"\nfunc (c *Client) UpdateBouncerLastPull(ctx context.Context, lastPull time.Time, id int) error {\n\t_, err := c.Ent.Bouncer.UpdateOneID(id).\n\t\tSetLastPull(lastPull).\n\t\tSave(ctx)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to update machine last pull in database: %w\", err)\n\t}\n\n\treturn nil\n}\n\nfunc (c *Client) UpdateBouncerStreamPull(ctx context.Context, lastPull time.Time, streamCursor int, id int) error {\n\t_, err := c.Ent.Bouncer.UpdateOneID(id).\n\t\tSetLastPull(lastPull).\n\t\tSetStreamCursor(streamCursor).\n\t\tSave(ctx)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to update bouncer stream pull in database: %w\", err)\n\t}\n\n\treturn nil\n}\n\nfunc (c *Client) UpdateBouncerIP(ctx context.Context, ipAddr string, id int) error {\n\t_, err := c.Ent.Bouncer.UpdateOneID(id).SetIPAddress(ipAddr).Save(ctx)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to update bouncer ip address in database: %w\", err)\n\t}\n\n\treturn nil\n}\n\nfunc (c *Client) UpdateBouncerTypeAndVersion(ctx context.Context, bType string, version string, id int) error {\n\t_, err := c.Ent.Bouncer.UpdateOneID(id).SetVersion(version).SetType(bType).Save(ctx)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to update bouncer type and version in database: %w\", err)","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/database/bouncers.go#L131-L167","documentation":"UpdateBouncerStreamPull wraps a failed ent update setting last_pull and the stream cursor on a bouncer row. Same mechanics as the last-pull update but also persists streamCursor; failures are wrapped with this message. Called by StreamDecision.","triggerScenarios":"Bouncer row missing (deleted concurrently), database locked or unreachable, context cancelled mid-update.","commonSituations":"Stream-mode bouncers polling frequently on a busy SQLite file hitting 'database is locked'; bouncer pruned while its stream session is live.","solutions":["Check SQLite lock contention / enable WAL mode for concurrent stream bouncers","Verify the bouncer was not deleted concurrently (ent.IsNotFound on the wrapped error)","Retry the update on transient driver errors","Ensure the request context survives the stream handler duration"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// ensure bouncer still exists before stream-cursor update\nif n, _ := c.Ent.Bouncer.Query().Where(bouncer.ID(id)).Count(ctx); n == 0 { return }","typeGuard":null,"tryCatchPattern":"err := c.UpdateBouncerStreamPull(ctx, lastPull, cursor, id)\nif errors.Is(errors.Unwrap(err), sqlite.ErrLocked) {\n\ttime.Sleep(backoff); retry()\n}","preventionTips":["Enable WAL mode for many concurrent stream bouncers","Use short, retried transactions for cursor updates","Prune conservatively so live bouncers aren't deleted","Watch for 'database is locked' in logs"],"tags":["database","ent","sqlite","bouncers","stream"],"backgroundTag":"database-write-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"}