{"record":{"id":"7409768816ce6a66","repo":"benbjohnson/litestream","slug":"parse-query-string-w","errorCode":null,"errorMessage":"parse query string: %w","messagePattern":"parse query string: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"replica_url.go","lineNumber":135,"sourceCode":"\tarnWithPath := s[len(prefix):]\n\n\t// Split off query string if present\n\tvar queryStr string\n\tif idx := strings.IndexByte(arnWithPath, '?'); idx != -1 {\n\t\tqueryStr = arnWithPath[idx+1:]\n\t\tarnWithPath = arnWithPath[:idx]\n\t}\n\n\tbucket, key, err := splitS3AccessPointARN(arnWithPath)\n\tif err != nil {\n\t\treturn \"\", \"\", \"\", nil, err\n\t}\n\n\t// Parse query string if present\n\tif queryStr != \"\" {\n\t\tquery, err = url.ParseQuery(queryStr)\n\t\tif err != nil {\n\t\t\treturn \"\", \"\", \"\", nil, fmt.Errorf(\"parse query string: %w\", err)\n\t\t}\n\t}\n\n\treturn \"s3\", bucket, CleanReplicaURLPath(key), query, nil\n}\n\n// splitS3AccessPointARN splits an S3 Access Point ARN into bucket and key components.\nfunc splitS3AccessPointARN(s string) (bucket, key string, err error) {\n\tlower := strings.ToLower(s)\n\tconst marker = \":accesspoint/\"\n\tidx := strings.Index(lower, marker)\n\tif idx == -1 {\n\t\treturn \"\", \"\", fmt.Errorf(\"invalid s3 access point arn: %s\", s)\n\t}\n\n\tnameStart := idx + len(marker)\n\tif nameStart >= len(s) {\n\t\treturn \"\", \"\", fmt.Errorf(\"invalid s3 access point arn: %s\", s)","sourceCodeStart":117,"sourceCodeEnd":153,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/replica_url.go#L117-L153","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Fix or remove the malformed query string after '?' in the URL.","Percent-encode special characters (use url.QueryEscape / QueryEscape for values).","Drop the query entirely if no parameters are needed for the replica.","Validate the URL with url.Parse/url.ParseQuery in config loading before starting replication."],"exampleFix":"// before\nu := \"s3://arn:aws:s3:us-east-1:123:accesspoint/ap/db?path=100%\" // parse query string error\n// after\nu := \"s3://arn:aws:s3:us-east-1:123:accesspoint/ap/db?path=\" + url.QueryEscape(\"100%\")","handlingStrategy":"validation","validationCode":"if i := strings.Index(replicaURL, \"?\"); i >= 0 {\n    if _, err := url.ParseQuery(replicaURL[i+1:]); err != nil { return fmt.Errorf(\"bad query in replica URL: %w\", err) }\n}","typeGuard":"func validQueryString(s string) bool { _, err := url.ParseQuery(s); return err == nil }","tryCatchPattern":"if _, err := litestream.ParseReplicaURLWithQuery(rawURL); err != nil {\n    if strings.Contains(err.Error(), \"parse query string\") { return fmt.Errorf(\"URL-encode the query values in %q\", rawURL) }\n    return err\n}","preventionTips":["Always url.QueryEscape query values appended to replica URLs.","Remove unneeded query strings from Access Point URLs.","Run url.ParseQuery on config URLs before starting replication.","Beware shell/templating corruption of '%' characters in query strings."],"tags":["s3","url","query-string","parsing"],"backgroundTag":"invalid-query-parameter","analyzedSha":"4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3","analyzedAt":"2026-09-06T18:29:25.564Z","contentChangedAt":"2026-09-06T18:29:25.564Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}