risingwavelabs/risingwave · warning

normalized URL isn't the same as the original one

Error message

normalized URL isn't the same as the original one

What it means

The meta dashboard HTTP proxy builds a URL on https://raw.githubusercontent.com/risingwavelabs/risingwave/dashboard-artifact plus the request path, parses it with url::Url, and then asserts that re-serializing the parsed URL yields the exact original string. If Url normalization changed the string (encoding, dot-segment removal, etc.), it refuses the request with this anyhow error as a safety check against unexpected/malicious paths.

Source

Thrown at src/meta/dashboard/src/proxy.rs:80

    uri: Uri,
    cache: Arc<Mutex<HashMap<String, CachedResponse>>>,
) -> anyhow::Result<Response> {
    let mut path = uri.path().to_owned();
    if path.ends_with('/') {
        path += "index.html";
    }

    if let Some(resp) = cache.lock().unwrap().get(&path) {
        return Ok(resp.clone().into_response());
    }

    let url_str = format!(
        "https://raw.githubusercontent.com/risingwavelabs/risingwave/dashboard-artifact{}",
        path
    );
    let url = Url::parse(&url_str)?;
    if url.to_string() != url_str {
        return Err(anyhow!("normalized URL isn't the same as the original one"));
    }

    tracing::info!("dashboard service: proxying {}", url);

    let content = reqwest::get(url.clone()).await?;

    let resp = CachedResponse {
        code: content.status(),
        headers: content.headers().clone(),
        body: content.bytes().await?,
        uri: url,
    };

    cache.lock().unwrap().insert(path, resp.clone());

    Ok(resp.into_response())
}

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Check the requested path for invalid characters, `..` segments, or percent-encoding that triggers normalization; request a plain artifact path instead.
  2. Verify the dashboard/proxy client URL matches the artifact file names published in the risingwave repo's dashboard-artifact branch.
  3. If the path is legitimate but normalized (e.g. contains spaces), URL-encode it client-side in a way that survives round-tripping, or fix the artifact file name.
  4. If you hit this as an internal invariant, ensure the `url` crate version is consistent (update Cargo.lock) and compare url.to_string() with the constructed string when debugging.

Example fix

// before: path with characters that get normalized
GET /dashboard/v2.0/some dir/index.html
// after: properly percent-encoded, round-trip-stable path
GET /dashboard/v2.0/some%20dir/index.html
Defensive patterns

Strategy: validation

Validate before calling

// Client side: send only round-trip-stable, percent-encoded paths
const path = encodeURI(rawPath).replaceAll('..', '');
if (!path.startsWith('/') || path.includes('//')) {
  throw new Error('invalid artifact path: ' + rawPath);
}
fetch(`/probe/artifact${path}`);

Type guard

fn is_round_trip_stable(path: &str, base: &str) -> bool {
    match Url::parse(&format!("{}{}", base, path)) {
        Ok(url) => url.as_str() == &format!("{}{}", base, path),
        Err(_) => false,
    }
}

Try / catch

// Rust: handle the anyhow error from the proxy handler
match proxy(path).await {
    Ok(resp) => resp,
    Err(e) if e.to_string().contains("normalized URL") => {
        StatusCode::BAD_REQUEST // reject malformed artifact paths
    }
    Err(e) => { tracing::error!(error = ?e, "proxy failed"); StatusCode::INTERNAL_SERVER_ERROR }
}

Prevention

When it happens

Trigger: A GET to the dashboard proxy endpoint whose path, after being appended to the base URL, normalizes differently — e.g. paths containing characters needing percent-encoding, `..` dot segments, empty segments, or a path that escapes the artifact prefix.

Common situations: Requesting a dashboard artifact file whose name contains special characters or encoded sequences; crafted URLs probing path traversal; a version of the `url` crate with different normalization rules; proxying to a branch/artifact path that no longer exists and redirects/normalizes oddly.

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 risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/3e4d941df102a674. Report an issue: GitHub.