jdx/mise · error · eyre::Report

URL must use s3:// scheme, got: {}

Error message

URL must use s3:// scheme, got: {}

What it means

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.

Source

Thrown at src/backend/s3.rs:77

use std::path::{Path, PathBuf};
use std::sync::Arc;
use tokio::sync::OnceCell;
use url::Url;

/// Parsed S3 URL components
#[derive(Debug, Clone)]
struct S3Url {
    bucket: String,
    key: String,
}

impl S3Url {
    /// Parse an S3 URL like "s3://bucket/path/to/object?region=us-west-2"
    fn parse(url_str: &str) -> Result<Self> {
        let url = Url::parse(url_str).map_err(|e| eyre!("Invalid S3 URL: {e}"))?;

        if url.scheme() != "s3" {
            bail!("URL must use s3:// scheme, got: {}", url.scheme());
        }

        let bucket = url
            .host_str()
            .ok_or_else(|| eyre!("S3 URL must include bucket name"))?
            .to_string();

        if bucket.is_empty() {
            bail!("S3 URL must include bucket name");
        }

        let key = url.path().trim_start_matches('/').to_string();

        Ok(Self { bucket, key })
    }
}

/// S3 backend for downloading tools from Amazon S3 or S3-compatible storage

View on GitHub (pinned to 6f52dcdf99)

Solutions

  1. Rewrite the URL as `s3://bucket/key.tar.gz` (path only, no scheme-specific host)
  2. If the asset is only reachable over HTTPS, use the http backend (`http://bucket/key.tar.gz`) or aqua/github instead of s3
  3. Verify the scheme string has no leading whitespace or smart characters

Example fix

# before
[tools.foo]
version = "1.0.0"
backend = "s3"
url = "https://my-bucket.s3.amazonaws.com/foo-1.0.0.tar.gz"

# after
[tools.foo]
version = "1.0.0"
backend = "s3"
url = "s3://my-bucket/foo-1.0.0.tar.gz"
Defensive patterns

Strategy: validation

Validate before calling

# Validate before install
python3 - <<'EOF'
from urllib.parse import urlparse
u = urlparse(open('mise.toml').read()) # or parse your url value
assert urlparse('s3://bucket/key').scheme == 's3'
EOF
# simpler: grep the url lines
! grep -n 'url[[:space:]]*=[[:space:]]*"https\?://' mise.toml

Type guard

fn is_s3_url(s: &str) -> bool {
    url::Url::parse(s).map(|u| u.scheme() == "s3").unwrap_or(false)
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of jdx/mise@6f52dcdf99 (2026-08-22). Data as JSON: /api/errors/3d97609ad659d2c4. Report an issue: GitHub.