{"record":{"id":"6eb6fa7f3da38201","repo":"crowdsecurity/crowdsec","slug":"could-not-parse-s3-args-w","errorCode":null,"errorMessage":"could not parse s3 args: %w","messagePattern":"could not parse s3 args: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/acquisition/modules/s3/config.go","lineNumber":218,"sourceCode":"\t}\n\n\ts.Config = Configuration{}\n\ts.logger = logger.WithFields(log.Fields{\n\t\t\"bucket\": s.Config.BucketName,\n\t\t\"prefix\": s.Config.Prefix,\n\t})\n\n\tdsn = strings.TrimPrefix(dsn, \"s3://\")\n\targs := strings.Split(dsn, \"?\")\n\n\tif args[0] == \"\" {\n\t\treturn errors.New(\"empty s3:// DSN\")\n\t}\n\n\tif len(args) == 2 && args[1] != \"\" {\n\t\tparams, err := url.ParseQuery(args[1])\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"could not parse s3 args: %w\", err)\n\t\t}\n\n\t\tfor key, value := range params {\n\t\t\tswitch key {\n\t\t\tcase \"log_level\":\n\t\t\t\tif len(value) != 1 {\n\t\t\t\t\treturn errors.New(\"expected zero or one value for 'log_level'\")\n\t\t\t\t}\n\n\t\t\t\tlvl, err := log.ParseLevel(value[0])\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"unknown level %s: %w\", value[0], err)\n\t\t\t\t}\n\n\t\t\t\ts.logger.Logger.SetLevel(lvl)\n\t\t\tcase \"max_buffer_size\":\n\t\t\t\tif len(value) != 1 {\n\t\t\t\t\treturn errors.New(\"expected zero or one value for 'max_buffer_size'\")","sourceCodeStart":200,"sourceCodeEnd":236,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/acquisition/modules/s3/config.go#L200-L236","documentation":"After the bucket part, ConfigureByDSN parses the DSN's query string with url.ParseQuery to extract options like log_level. A malformed query — invalid percent-encoding or malformed key=value pairs — is wrapped as 'could not parse s3 args'.","triggerScenarios":"DSNs such as s3://bucket?prefix=logs%zz (invalid escape), s3://bucket?;; or s3://bucket?a=% where url.ParseQuery returns an error.","commonSituations":"Hand-rolled percent-encoding producing invalid escapes like %zz; literal '%' not encoded as %25; truncated or concatenated DSN strings in generated configs.","solutions":["Fix the query string: use url.QueryEscape (or equivalent) to encode values, e.g. prefix=logs%2Fapp","Encode literal '%' as %25 and remove stray separators not forming key=value pairs","Join multiple options with & and ensure each is key=value","Pre-validate the DSN with url.ParseQuery in the code that builds it"],"exampleFix":"// before\n\"s3://my-bucket?prefix=logs%zz\"\n// after\n\"s3://my-bucket?prefix=logs%2Fapp\"","handlingStrategy":"validation","validationCode":"parts := strings.SplitN(strings.TrimPrefix(dsn, \"s3://\"), \"?\", 2)\nif len(parts) == 2 {\n    if _, err := url.ParseQuery(parts[1]); err != nil {\n        // reject/fix the DSN before ConfigureByDSN\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Build query strings with url.Values.Encode() instead of string concatenation","Percent-encode values with url.QueryEscape; encode literal % as %25","Keep one ? separator and join options with &"],"tags":["s3","dsn","url"],"backgroundTag":"invalid-query-parameter","analyzedSha":"909b5157986a2b2c2163300fdaef5ed01289f7d2","analyzedAt":"2026-09-06T12:27:26.012Z","contentChangedAt":"2026-09-06T12:27:26.012Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}