thanos-io/thanos · error

parse raw query

Error message

parse raw query %s

What it means

QueryInstant parses the base URL's existing RawQuery with url.ParseQuery before adding the query/time parameters. If that raw query string is malformed (bad percent-encoding, invalid separator), it returns this error wrapping the parse failure and including the raw query string.

Solutions

  1. Inspect the raw query printed in the error to find the malformed segment
  2. Use url.Values.Encode() or url.Parse on the full base URL instead of hand-built query strings
  3. Strip or fix the base URL's RawQuery if pre-existing params are not needed
  4. Percent-encode any dynamically inserted query values

Example fix

// before
u, _ := url.Parse("http://thanos:9090/api/v1/query?query=%zz") // invalid escape
// after
u, _ := url.Parse("http://thanos:9090/api/v1/query") // let the client add params
Defensive patterns

Strategy: validation

Validate before calling

if base.RawQuery != "" {
    if _, err := url.ParseQuery(base.RawQuery); err != nil {
        return fmt.Errorf("base url has malformed query: %w", err)
    }
}

Type guard

func hasValidRawQuery(u *url.URL) bool {
    _, err := url.ParseQuery(u.RawQuery)
    return err == nil
}

Try / catch

vec, _, _, err := client.QueryInstant(ctx, base, query, t, opts)
if err != nil && strings.Contains(err.Error(), "parse raw query") {
    base.RawQuery = "" // drop malformed params and retry
    vec, _, _, err = client.QueryInstant(ctx, base, query, t, opts)
}

Prevention

When it happens

Trigger: The *url.URL passed to QueryInstant contains a RawQuery that url.ParseQuery rejects — e.g. '%' not followed by hex digits, or invalid separators left over from manual URL construction.

Common situations: Building base URLs by string concatenation with hand-written query strings; copying URLs from logs/browsers with encoded characters mishandled; templates injecting unencoded values into the query part.

Understand the failure class

Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.

Related errors


AI-assisted analysis of thanos-io/thanos@35b8b99117 (2026-09-07). Data as JSON: /api/errors/dcc2029f45feba57. Report an issue: GitHub.

Appendix: source

Thrown at pkg/promclient/promclient.go:430

		return errors.Errorf("unknown partial response strategy %v", p.PartialResponseStrategy)
	}

	// TODO(bwplotka): Apply change from bool to strategy in Query API as well.
	values.Add("partial_response", partialResponseValue)

	return nil
}

type Explanation struct {
	Name     string         `json:"name"`
	Children []*Explanation `json:"children,omitempty"`
}

// QueryInstant performs an instant query using a default HTTP client and returns results in model.Vector type.
func (c *Client) QueryInstant(ctx context.Context, base *url.URL, query string, t time.Time, opts QueryOptions) (model.Vector, []string, *Explanation, error) {
	params, err := url.ParseQuery(base.RawQuery)
	if err != nil {
		return nil, nil, nil, errors.Wrapf(err, "parse raw query %s", base.RawQuery)
	}
	params.Add("query", query)
	params.Add("time", t.Format(time.RFC3339Nano))
	if !opts.DoNotAddThanosParams {
		if err := opts.AddTo(params); err != nil {
			return nil, nil, nil, errors.Wrap(err, "add thanos opts query params")
		}
	}

	u := *base
	u.Path = path.Join(u.Path, "/api/v1/query")
	u.RawQuery = params.Encode()

	level.Debug(c.logger).Log("msg", "querying instant", "url", u.String())

	span, ctx := tracing.StartSpan(ctx, "/prom_query_instant HTTP[client]")
	defer span.Finish()

View on GitHub (pinned to 35b8b99117)