benbjohnson/litestream · error

parse query string: %w

Error message

parse query string: %w

What it means

After splitting the query string off an S3 Access Point URL, parseS3AccessPointURL parses it with url.ParseQuery; malformed query components are wrapped as "parse query string". This means the s3://arn:... URL's query portion is not valid URL-encoded key=value pairs.

Source

Thrown at replica_url.go:135

	arnWithPath := s[len(prefix):]

	// Split off query string if present
	var queryStr string
	if idx := strings.IndexByte(arnWithPath, '?'); idx != -1 {
		queryStr = arnWithPath[idx+1:]
		arnWithPath = arnWithPath[:idx]
	}

	bucket, key, err := splitS3AccessPointARN(arnWithPath)
	if err != nil {
		return "", "", "", nil, err
	}

	// Parse query string if present
	if queryStr != "" {
		query, err = url.ParseQuery(queryStr)
		if err != nil {
			return "", "", "", nil, fmt.Errorf("parse query string: %w", err)
		}
	}

	return "s3", bucket, CleanReplicaURLPath(key), query, nil
}

// splitS3AccessPointARN splits an S3 Access Point ARN into bucket and key components.
func splitS3AccessPointARN(s string) (bucket, key string, err error) {
	lower := strings.ToLower(s)
	const marker = ":accesspoint/"
	idx := strings.Index(lower, marker)
	if idx == -1 {
		return "", "", fmt.Errorf("invalid s3 access point arn: %s", s)
	}

	nameStart := idx + len(marker)
	if nameStart >= len(s) {
		return "", "", fmt.Errorf("invalid s3 access point arn: %s", s)

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Fix or remove the malformed query string after '?' in the URL.
  2. Percent-encode special characters (use url.QueryEscape / QueryEscape for values).
  3. Drop the query entirely if no parameters are needed for the replica.
  4. Validate the URL with url.Parse/url.ParseQuery in config loading before starting replication.

Example fix

// before
u := "s3://arn:aws:s3:us-east-1:123:accesspoint/ap/db?path=100%" // parse query string error
// after
u := "s3://arn:aws:s3:us-east-1:123:accesspoint/ap/db?path=" + url.QueryEscape("100%")
Defensive patterns

Strategy: validation

Validate before calling

if i := strings.Index(replicaURL, "?"); i >= 0 {
    if _, err := url.ParseQuery(replicaURL[i+1:]); err != nil { return fmt.Errorf("bad query in replica URL: %w", err) }
}

Type guard

func validQueryString(s string) bool { _, err := url.ParseQuery(s); return err == nil }

Try / catch

if _, err := litestream.ParseReplicaURLWithQuery(rawURL); err != nil {
    if strings.Contains(err.Error(), "parse query string") { return fmt.Errorf("URL-encode the query values in %q", rawURL) }
    return err
}

Prevention

When it happens

Trigger: Passing an s3://arn:... URL whose query string fails url.ParseQuery — e.g. a bare '%' escape like ?path=100%, or invalid percent-encodings after '?' in the Access Point URL.

Common situations: Unescaped '%' in paths/keys appended to Access Point URLs; shell or templating corruption of query strings; hand-built URLs missing URL-encoding of special characters.

Understand the failure class

Background: "Invalid query parameter" / "Failed to parse value of ...": fixing bad query string parameters across APIs — this error's family across 36 libraries.

Related errors


AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06). Data as JSON: /api/errors/7409768816ce6a66. Report an issue: GitHub.