crowdsecurity/crowdsec · error

error decoding Loki response: %w

Error message

error decoding Loki response: %w

What it means

queryRange received HTTP 200 but the body could not be decoded into LokiQueryRangeResponse (invalid or structurally unexpected JSON). After retries are exhausted, the decode error is wrapped and returned.

Source

Thrown at pkg/acquisition/modules/loki/internal/lokiclient/loki_client.go:160

				continue
			}

			if resp.StatusCode != http.StatusOK {
				lc.Logger.Warnf("bad HTTP response code for query range: %d", resp.StatusCode)
				body, _ := io.ReadAll(resp.Body)
				resp.Body.Close()
				if ok := lc.shouldRetry(); !ok {
					return fmt.Errorf("bad HTTP response code: %d: %s: %w", resp.StatusCode, string(body), err)
				}
				lc.increaseTicker(ticker)
				continue
			}

			var lq LokiQueryRangeResponse
			if err := json.NewDecoder(resp.Body).Decode(&lq); err != nil {
				resp.Body.Close()
				if ok := lc.shouldRetry(); !ok {
					return fmt.Errorf("error decoding Loki response: %w", err)
				}
				lc.increaseTicker(ticker)
				continue
			}
			resp.Body.Close()
			lc.Logger.Tracef("Got response: %+v", lq)
			c <- &lq
			lc.resetFailStart()
			if !infinite && (len(lq.Data.Result) == 0 || len(lq.Data.Result[0].Entries) < lc.config.Limit) {
				lc.Logger.Infof("Got less than %d results (%d), stopping", lc.config.Limit, len(lq.Data.Result))
				close(c)
				return nil
			}
			if len(lq.Data.Result) > 0 {
				lc.Logger.Debugf("(timer:%v) %d results / %d entries result[0] (uri:%s)", lc.currentTickerInterval, len(lq.Data.Result), len(lq.Data.Result[0].Entries), uri)
			} else {
				lc.Logger.Debugf("(timer:%v) no results (uri:%s)", lc.currentTickerInterval, uri)
			}

View on GitHub (pinned to 909b515798)

Solutions

  1. Inspect the raw response body via curl to see what Loki actually returns
  2. Check for auth redirects/proxies in front of Loki
  3. Align Loki version with the expected query_range response schema
Defensive patterns

Strategy: validation

Validate before calling

// probe raw body first:
// body, _ := io.ReadAll(resp); var probe map[string]any
// if json.Unmarshal(body, &probe) != nil || probe["data"] == nil { /* not a Loki API response */ }

Try / catch

if err := json.NewDecoder(resp.Body).Decode(&lq); err != nil {
    log.Errorf("non-JSON or unexpected Loki response; check for auth portal/proxy: %v", err)
    return err
}

Prevention

When it happens

Trigger: Loki returns HTML (auth portal, error page) with 200; an intermediate proxy mangles the body; a Loki version returns a different JSON schema.

Common situations: Basic-auth login pages returned with 200; reverse proxies intercepting requests; version mismatch between expected and actual Loki API response fields.

Understand the failure class

Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.

Related errors


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