{"record":{"id":"3d97609ad659d2c4","repo":"jdx/mise","slug":"url-must-use-s3-scheme-got","errorCode":null,"errorMessage":"URL must use s3:// scheme, got: {}","messagePattern":"URL must use s3:// scheme, got: (.+?)","errorType":"validation","errorClass":"eyre::Report","httpStatus":null,"severity":"error","filePath":"src/backend/s3.rs","lineNumber":77,"sourceCode":"use std::path::{Path, PathBuf};\nuse std::sync::Arc;\nuse tokio::sync::OnceCell;\nuse url::Url;\n\n/// Parsed S3 URL components\n#[derive(Debug, Clone)]\nstruct S3Url {\n    bucket: String,\n    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","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/jdx/mise/blob/6f52dcdf99e282ef7a7db68c81301fa4618d0f79/src/backend/s3.rs#L59-L95","documentation":"The s3:// backend parses tool URLs with S3Url::parse, which accepts only the `s3://bucket/key` form. Any URL whose scheme is not `s3` (typically `https://` or `http://`) is rejected before any download starts.","triggerScenarios":"Declaring a tool with `backend = \"s3\"` (or `s3:` shorthand) but setting `url = \"https://bucket.s3.amazonaws.com/key.tar.gz\"`. Also triggered by a typo like `s4://` or an empty url that happens to parse with a different default scheme.","commonSituations":"Copying a presigned or public HTTPS S3 link from the AWS console into mise.toml, or migrating an http backend entry to s3 by editing only the host part.","solutions":["Rewrite the URL as `s3://bucket/key.tar.gz` (path only, no scheme-specific host)","If the asset is only reachable over HTTPS, use the http backend (`http://bucket/key.tar.gz`) or aqua/github instead of s3","Verify the scheme string has no leading whitespace or smart characters"],"exampleFix":"# before\n[tools.foo]\nversion = \"1.0.0\"\nbackend = \"s3\"\nurl = \"https://my-bucket.s3.amazonaws.com/foo-1.0.0.tar.gz\"\n\n# after\n[tools.foo]\nversion = \"1.0.0\"\nbackend = \"s3\"\nurl = \"s3://my-bucket/foo-1.0.0.tar.gz\"","handlingStrategy":"validation","validationCode":"# Validate before install\npython3 - <<'EOF'\nfrom urllib.parse import urlparse\nu = urlparse(open('mise.toml').read()) # or parse your url value\nassert urlparse('s3://bucket/key').scheme == 's3'\nEOF\n# simpler: grep the url lines\n! grep -n 'url[[:space:]]*=[[:space:]]*\"https\\?://' mise.toml","typeGuard":"fn is_s3_url(s: &str) -> bool {\n    url::Url::parse(s).map(|u| u.scheme() == \"s3\").unwrap_or(false)\n}","tryCatchPattern":null,"preventionTips":["Use s3://bucket/key form for the s3 backend; use the http backend for plain URLs","Script a config lint that checks backend/url scheme pairs in mise.toml","Prefer aqua: shorthand when the tool is in the aqua registry"],"tags":["s3","url-parsing","config-validation","mise-toml"],"backgroundTag":"invalid-url-scheme","analyzedSha":"6f52dcdf99e282ef7a7db68c81301fa4618d0f79","analyzedAt":"2026-08-22T10:14:23.840Z","schemaVersion":2},"datasetVersion":"2026-08-30T08:17:16.595Z"}