crowdsecurity/crowdsec · error

unable to update machine last pull in database: %w

Error message

unable to update machine last pull in database: %w

What it means

UpdateBouncerLastPull wraps a failed ent update of a bouncer's last_pull timestamp. It calls UpdateOneID(id).SetLastPull(...).Save(ctx); any driver error, missing row, or cancelled context surfaces wrapped in this message. Called by GetDecision after a bouncer pulls decisions.

Source

Thrown at pkg/database/bouncers.go:137

	ids := make([]int, len(bouncers))
	for i, b := range bouncers {
		ids[i] = b.ID
	}

	nbDeleted, err := c.Ent.Bouncer.Delete().Where(bouncer.IDIn(ids...)).Exec(ctx)
	if err != nil {
		return nbDeleted, fmt.Errorf("unable to delete bouncers: %w", err)
	}

	return nbDeleted, nil
}

func (c *Client) UpdateBouncerLastPull(ctx context.Context, lastPull time.Time, id int) error {
	_, err := c.Ent.Bouncer.UpdateOneID(id).
		SetLastPull(lastPull).
		Save(ctx)
	if err != nil {
		return fmt.Errorf("unable to update machine last pull in database: %w", err)
	}

	return nil
}

func (c *Client) UpdateBouncerStreamPull(ctx context.Context, lastPull time.Time, streamCursor int, id int) error {
	_, err := c.Ent.Bouncer.UpdateOneID(id).
		SetLastPull(lastPull).
		SetStreamCursor(streamCursor).
		Save(ctx)
	if err != nil {
		return fmt.Errorf("unable to update bouncer stream pull in database: %w", err)
	}

	return nil
}

func (c *Client) UpdateBouncerIP(ctx context.Context, ipAddr string, id int) error {

View on GitHub (pinned to 909b515798)

Solutions

  1. Confirm the bouncer still exists (it may have been pruned or deleted between auth and update)
  2. Check for SQLite lock contention from other crowdsec processes; consider WAL mode or retrying
  3. Inspect the wrapped driver error and retry transient failures
  4. Verify the request context is not being cancelled early by the HTTP server

Example fix

// before
_, err := c.Ent.Bouncer.UpdateOneID(id).SetLastPull(lastPull).Save(ctx)
if err != nil {
	return fmt.Errorf("unable to update machine last pull in database: %w", err)
}
// after
nb, err := c.Ent.Bouncer.UpdateOneID(id).SetLastPull(lastPull).Save(ctx)
if ent.IsNotFound(err) {
	return fmt.Errorf("bouncer %d no longer exists", id)
} else if err != nil {
	return fmt.Errorf("unable to update machine last pull in database: %w", err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// before calling: ensure the bouncer exists
exists, err := c.BouncerExistsByID(ctx, id)
if err != nil || !exists { return }

Try / catch

if err := c.UpdateBouncerLastPull(ctx, time.Now(), id); err != nil {
	if ent.IsNotFound(errors.Unwrap(err)) { /* bouncer pruned; re-register or skip */ }
	log.Warnf("last pull update failed: %v", err)
}

Prevention

When it happens

Trigger: The bouncer ID no longer exists in the bouncers table, the database is unreachable/locked, or the request context was cancelled before the UPDATE committed.

Common situations: Bouncer deleted by prune while it was still pulling decisions, SQLite 'database is locked' under concurrent LAPI traffic, transient MySQL connection reset.

Related errors


AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06). Data as JSON: /api/errors/79cdee5b4da9f9ef. Report an issue: GitHub.