{"record":{"id":"5846e30b30f63420","repo":"crowdsecurity/crowdsec","slug":"unable-to-update-machine-in-database-w","errorCode":null,"errorMessage":"unable to update machine in database: %w","messagePattern":"unable to update machine in database: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/database/machines.go","lineNumber":207,"sourceCode":"\treturn nbDeleted, nil\n}\n\nfunc (c *Client) UpdateMachineLastHeartBeat(ctx context.Context, machineID string) error {\n\t_, err := c.Ent.Machine.Update().Where(machine.MachineIdEQ(machineID)).SetLastHeartbeat(time.Now().UTC()).Save(ctx)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"updating machine last_heartbeat: %w: %w\", err, UpdateFail)\n\t}\n\n\treturn nil\n}\n\nfunc (c *Client) UpdateMachineScenarios(ctx context.Context, scenarios string, id int) error {\n\t_, err := c.Ent.Machine.UpdateOneID(id).\n\t\tSetUpdatedAt(time.Now().UTC()).\n\t\tSetScenarios(scenarios).\n\t\tSave(ctx)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to update machine in database: %w\", err)\n\t}\n\n\treturn nil\n}\n\nfunc (c *Client) UpdateMachineIP(ctx context.Context, ipAddr string, id int) error {\n\t_, err := c.Ent.Machine.UpdateOneID(id).\n\t\tSetIpAddress(ipAddr).\n\t\tSave(ctx)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to update machine IP in database: %w\", err)\n\t}\n\n\treturn nil\n}\n\nfunc (c *Client) UpdateMachineVersion(ctx context.Context, ipAddr string, id int) error {\n\t_, err := c.Ent.Machine.UpdateOneID(id).","sourceCodeStart":189,"sourceCodeEnd":225,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/database/machines.go#L189-L225","documentation":"UpdateMachineScenarios returns the raw ent error (no sentinel) when Machine.UpdateOneID(id) fails while persisting the machine's scenario list. This is called during Authenticator, i.e. when a validated watcher authenticates; a stale/unknown row id makes ent return a not-found error here. Note the message text is slightly off — it's a generic update failure, not 'machine' specific.","triggerScenarios":"Authenticator detects the machine's scenario list changed and calls UpdateMachineScenarios, but the row with the given auto-increment id was deleted concurrently, or the DB write fails (lock, connection).","commonSituations":"Machine deleted (prune/`cscli machines delete`) while its heartbeat/auth was in flight; DB busy on SQLite; id passed from a stale cached row.","solutions":["Re-check the machine still exists (QueryMachineByID) before/after the update and handle deletion races gracefully","Inspect the wrapped error: ent not-found vs connection failure lead to different fixes","If races with prune/delete are common, re-fetch the machine row and retry once","Ensure DB is writable and the schema matches (`cscli migration`)"],"exampleFix":"// before\nif err := db.UpdateMachineScenarios(ctx, scenarios, machine.ID); err != nil {\n    return err\n}\n// after\nif err := db.UpdateMachineScenarios(ctx, scenarios, machine.ID); err != nil {\n    if _, qerr := db.QueryMachineByID(ctx, machine.MachineId); errors.Is(qerr, database.UserNotExists) {\n        return nil // machine deleted concurrently; nothing to update\n    }\n    return err\n}","handlingStrategy":"try-catch","validationCode":"// refresh the row before the id-based update\nm, err := db.QueryMachineByID(ctx, machineID)\nif err != nil { return err }\nerr = db.UpdateMachineScenarios(ctx, scenarios, m.ID)","typeGuard":null,"tryCatchPattern":"if err := db.UpdateMachineScenarios(ctx, scenarios, id); err != nil {\n    if _, qerr := db.QueryMachineByID(ctx, machineID); errors.Is(qerr, database.UserNotExists) {\n        return nil // benign: machine deleted concurrently\n    }\n    return err\n}","preventionTips":["Re-fetch machine rows before id-based updates in auth hot paths","Avoid pruning/deleting machines while auth requests are in flight (or handle the race)","Treat ent not-found on these updates as benign and log at debug level","Check HandleDBErrors (pkg/apiserver/controllers/v1/errors.go) to map these to correct HTTP codes"],"tags":["database","update-failed","crowdsec","authenticator"],"backgroundTag":"record-not-found","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"}