{"record":{"id":"1f93caf983d84978","repo":"jdx/mise","slug":"s3-url-must-include-bucket-name","errorCode":null,"errorMessage":"S3 URL must include bucket name","messagePattern":"S3 URL must include bucket name","errorType":"validation","errorClass":"eyre::Report","httpStatus":null,"severity":"error","filePath":"src/backend/s3.rs","lineNumber":86,"sourceCode":"    key: String,\n}\n\nimpl S3Url {\n    /// Parse an S3 URL like \"s3://bucket/path/to/object?region=us-west-2\"\n    fn parse(url_str: &str) -> Result<Self> {\n        let url = Url::parse(url_str).map_err(|e| eyre!(\"Invalid S3 URL: {e}\"))?;\n\n        if url.scheme() != \"s3\" {\n            bail!(\"URL must use s3:// scheme, got: {}\", url.scheme());\n        }\n\n        let bucket = url\n            .host_str()\n            .ok_or_else(|| eyre!(\"S3 URL must include bucket name\"))?\n            .to_string();\n\n        if bucket.is_empty() {\n            bail!(\"S3 URL must include bucket name\");\n        }\n\n        let key = url.path().trim_start_matches('/').to_string();\n\n        Ok(Self { bucket, key })\n    }\n}\n\n/// S3 backend for downloading tools from Amazon S3 or S3-compatible storage\n#[derive(Debug)]\npub(crate) struct S3Backend {\n    ba: Arc<BackendArg>,\n    /// Cached S3 client, lazily initialized\n    client: OnceCell<S3Client>,\n}\n\n#[derive(Debug, Clone, Copy)]\nstruct S3Options<'a> {","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/jdx/mise/blob/6f52dcdf99e282ef7a7db68c81301fa4618d0f79/src/backend/s3.rs#L68-L104","documentation":"S3Url::parse requires a non-empty host component as the bucket name. This error fires when the `s3://` URL has no host at all (host_str() is None) or an empty one — i.e. the bucket was omitted.","triggerScenarios":"Writing `url = \"s3:///path/to/asset.tar.gz\"` (three slashes) or a URL where the bucket position is empty. It is the immediate follow-on check after the s3 scheme check passes.","commonSituations":"Editing an s3:// URL and deleting the bucket, template-rendering the URL with an empty bucket variable, or copy-paste that loses the bucket segment.","solutions":["Add the bucket: `s3://my-bucket/path/to/asset.tar.gz`","Check template variables (e.g. `s3://{{bucket}}/key`) actually render a non-empty value","Use exactly two slashes after s3: when the bucket follows directly"],"exampleFix":"# before\nurl = \"s3:///releases/tool-1.0.0.tar.gz\"\n\n# after\nurl = \"s3://my-bucket/releases/tool-1.0.0.tar.gz\"","handlingStrategy":"validation","validationCode":"case \"$url\" in s3://?*/) echo ok;; *) echo \"bad s3 url: $url\";; esac","typeGuard":"fn has_s3_bucket(s: &str) -> bool {\n    url::Url::parse(s).map(|u| u.host_str().is_some_and(|h| !h.is_empty())).unwrap_or(false)\n}","tryCatchPattern":null,"preventionTips":["Never use three slashes: s3:///key is missing the bucket","Render templates with a non-empty bucket variable and assert on it in CI"],"tags":["s3","url-parsing","config-validation","mise-toml"],"backgroundTag":"malformed-url","analyzedSha":"6f52dcdf99e282ef7a7db68c81301fa4618d0f79","analyzedAt":"2026-08-22T10:14:23.840Z","schemaVersion":2},"datasetVersion":"2026-08-30T08:17:16.595Z"}