denoland/deno · error

package name contains a URL path or delimiter character

Error message

package name contains a URL path or delimiter character

What it means

`parse_package_name` rejects scope or package components containing URL path or delimiter characters — `/`, `\`, `?`, `#`, `%` — because these characters would change how the name is embedded into registry URLs (path separators, query, fragment, percent-encoding) and enable name smuggling. If either component contains any of them, this error is raised.

Source

Thrown at cli/registry.rs:252

    })?;
  if reference.sub_path().is_some() {
    bail!("package name must not contain additional path segments");
  }

  let Some((scope, package)) =
    name.strip_prefix('@').and_then(|name| name.split_once('/'))
  else {
    bail!("package name must use the '@<scope>/<package>' format");
  };
  for component in [scope, package] {
    if component == "." || component == ".." {
      bail!("package name must not contain dot path segments");
    }
    if component
      .chars()
      .any(|c| matches!(c, '/' | '\\' | '?' | '#' | '%'))
    {
      bail!("package name contains a URL path or delimiter character");
    }
  }
  Ok((scope, package))
}

/// Returns `true` if the given package version is already published to the
/// registry.
///
/// Only a `200 OK` response is treated as "already published". A `404` (and any
/// other non-success status) is treated as "not published" so that this
/// up-front optimization never blocks a legitimate publish because of a
/// transient registry error.
pub async fn check_version_exists(
  client: &HttpClient,
  registry_api_url: &Url,
  scope: &str,
  package: &str,
  version: &str,

View on GitHub (pinned to f7822238ca)

Solutions

  1. Strip the URL/query/fragment portion and pass only the bare `@<scope>/<package>` identifier
  2. Percent-decode the string first if it may be URL-encoded, then re-validate
  3. Sanitize the name to remove `/`, `\`, `?`, `#`, `%` or reject it before calling

Example fix

// before
parse_package_name("@my-scope/my-pkg?version=1")?;
// after
parse_package_name("@my-scope/my-pkg")?;
Defensive patterns

Strategy: validation

Validate before calling

const RESERVED = /[\/\\?#%]/;
function is_clean_jsr_name(name) {
  if (!name.startsWith("@")) return false;
  const [, rest] = [name.slice(1), name.slice(1)];
  const [scope, pkg] = rest.split("/");
  if (!scope || !pkg || pkg.includes("/")) return false;
  return !RESERVED.test(scope) && !RESERVED.test(pkg);
}
if (!is_clean_jsr_name(name)) throw new Error("strip URL delimiters from the package name");

Type guard

fn has_no_url_delimiters(name: &str) -> bool {
  !name.chars().any(|c| matches!(c, '/' | '\\' | '?' | '#' | '%'))
}

Try / catch

match parse_package_name(name) {
  Ok((scope, package)) => /* use scope/package */,
  Err(e) if e.to_string().contains("URL path or delimiter") => {
    eprintln!("pass the bare name, not a URL: got {name:?}");
  }
  Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Passing a name such as `@scope/pkg?x=1`, `@scope/sub/pkg`, `@scope/pkg%20x`, or `@scope/pkg#frag` into `parse_package_name`; also names that survived URL decoding/encoding rounds and still carry reserved characters.

Common situations: Passing a full URL or URL fragment instead of a bare package name; double-splitting a name so a second `/` lands inside the package component; names read from query strings or configs without URL-decoding; Windows paths leaking backslashes.

Related errors


AI-assisted analysis of denoland/deno@f7822238ca (2026-08-29). Data as JSON: /api/errors/cf54a4fb38bb5fef. Report an issue: GitHub.