databendlabs/databend · error

path in URL must end with '/

Error message

path in URL must end with '/' {usage}. Got '{}'.

What it means

parse_storage_params_from_uri requires that the path portion of the URI ends with a slash ('/') so it can be treated as a prefix/root by OpenDAL. If the URI path does not end with '/', it throws this InvalidInput error with the offending path and usage context embedded in the message.

Solutions

  1. Append a trailing '/' to the URL path, e.g. URL='s3://bucket/prefix/'
  2. If you intended the bucket root, use 's3://bucket/'
  3. Fix URI-building code to always end the path component with '/'

Example fix

-- before
CREATE CONNECTION c STORAGE_TYPE='s3' URL='s3://mybucket/data';
-- after
CREATE CONNECTION c STORAGE_TYPE='s3' URL='s3://mybucket/data/';
Defensive patterns

Strategy: validation

Validate before calling

if let Some(p) = url::Url::parse(storage_url)?.path() { /* ensure trailing slash */ }
let path = url::Url::parse(storage_url)?.path().to_string();
if !path.ends_with('/') && !path.is_empty() {
    return Err(format!("storage URL path must end with '/': {storage_url}"));
}

Try / catch

match err.kind() { ErrorKind::InvalidInput if msg.contains("path in URL must end with") => /* append '/' and retry */, _ => return Err(err) }

Prevention

When it happens

Trigger: CREATE CONNECTION with URL='s3://bucket/prefix' (no trailing slash), or stage/connection DDL where the path component is a bare prefix; same for resolve_storage_params_from_uri callers.

Common situations: Writing 's3://bucket/data' instead of 's3://bucket/data/'; generating URIs programmatically and dropping the trailing slash.

Understand the failure class

Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.

Related errors


AI-assisted analysis of databendlabs/databend@288d84d76e (2026-09-11). Data as JSON: /api/errors/bf8b7a1451e58423. Report an issue: GitHub.

Appendix: source

Thrown at src/query/sql/src/planner/binder/location.rs:504

        token: l.connection.get("token").cloned().unwrap_or_default(),
        network_config: None,
    });

    l.connection
        .check()
        .map_err(|err| Error::new(ErrorKind::InvalidInput, err.to_string()))?;

    Ok(sp)
}

pub async fn parse_storage_params_from_uri(
    l: &mut UriLocation,
    usage: &str,
) -> Result<StorageParams> {
    if !l.path.ends_with('/') {
        return Err(Error::new(
            ErrorKind::InvalidInput,
            anyhow!("path in URL must end with '/' {usage}. Got '{}'.", l.path),
        ));
    }
    Ok(parse_uri_location(l).await?.0)
}

struct ParsedUriLocation {
    root: String,
    path: String,
    protocol: Scheme,
}

fn parse_uri_location_parts(l: &UriLocation) -> Result<ParsedUriLocation> {
    // Path ends with `/` means it's a directory, otherwise it's a file.
    // If the path is a directory, we will use this path as root.
    // If the path is a file, we will use `/` as root (which is the default value)
    let (root, path) = if l.path.ends_with('/') {
        (l.path.clone(), "/".to_string())
    } else {

View on GitHub (pinned to 288d84d76e)