crowdsecurity/crowdsec · error · QueryFail

latest decision id: %w

Error message

latest decision id: %w

What it means

Returned by Client.LatestDecisionID when the query fetching the max decision ID fails (a genuine 'not found' yields 0,nil instead). It wraps the QueryFail sentinel ("unable to query"); the driver error is logged as 'LatestDecisionID : <err>'.

Source

Thrown at pkg/database/decisions.go:69

		return []*ent.Decision{}, fmt.Errorf("get all decisions with filters: %w", QueryFail)
	}

	return data, nil
}

func (c *Client) LatestDecisionID(ctx context.Context) (int, error) {
	latest, err := c.Ent.Decision.Query().
		Select(decision.FieldID).
		Order(ent.Desc(decision.FieldID)).
		First(ctx)
	if err != nil {
		if ent.IsNotFound(err) {
			return 0, nil
		}

		c.Log.Warningf("LatestDecisionID : %s", err)

		return 0, fmt.Errorf("latest decision id: %w", QueryFail)
	}

	return latest.ID, nil
}

func (c *Client) QueryExpiredDecisionsWithFilters(ctx context.Context, now time.Time, filter map[string][]string) ([]*ent.Decision, error) {
	query := c.Ent.Decision.Query().
		Select(decision.FieldID, decision.FieldUntil, decision.FieldScenario, decision.FieldScope, decision.FieldValue, decision.FieldType, decision.FieldOrigin, decision.FieldUUID).
		Where(
			decision.UntilLT(now),
		)
	// Allow a bouncer to ask for non-deduplicated results
	if v, ok := filter["dedup"]; !ok || v[0] != "false" {
		query = query.Where(longestDecisionForScopeTypeValue)
	}

	query, err := applyDecisionFilter(query, filter)
	if err != nil {

View on GitHub (pinned to 909b515798)

Solutions

  1. Check LAPI logs for the 'LatestDecisionID :' warning to identify the driver error
  2. If LAPI starts before the DB, add a healthcheck/startup dependency (docker-compose depends_on: condition: service_healthy)
  3. Verify DB connectivity with the credentials in crowdsec's db config (`cscli db status` equivalent / direct client connect)
  4. Restore write permissions on the SQLite file/mount if it is read-only

Example fix

// docker-compose before
services:
  crowdsec:
    image: crowdsecurity/crowdsec
// after
services:
  db:
    image: mariadb
    healthcheck: {test: ["CMD", "healthcheck.sh"], interval: 5s}
  crowdsec:
    image: crowdsecurity/crowdsec
    depends_on:
      db: {condition: service_healthy}
Defensive patterns

Strategy: try-catch

Validate before calling

// before polling, verify DB reachability
if err := sqlDB.Ping(); err != nil { log.Fatalf("database unreachable: %v", err) }

Try / catch

id, err := client.LatestDecisionID(ctx)
if err != nil {
    // sentinel wraps 'unable to query'; inspect LAPI log 'LatestDecisionID :'
    log.Warnf("latest decision id unavailable, falling back to full pull: %v", err)
    id = 0 // forces full stream sync
}

Prevention

When it happens

Trigger: The last-ID SELECT fails due to DB unavailability — connection refused/timeout to MySQL/PostgreSQL, SQLite lock, missing decisions table, or driver error. Called by StreamDecision (stream pulling) and TestLatestDecisionID.

Common situations: LAPI starts before the database container is ready (docker-compose ordering), so every stream request fails with this until DB is up; SQLite file on a read-only mount after restore.

Understand the failure class

Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.

Related errors


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