nikivdev/code · error · anyhow::Error

bun, npx, or npm is required to run wrangler

Error message

bun, npx, or npm is required to run wrangler

What it means

attach_pages_domain needs the wrangler CLI (Cloudflare Pages) to add a custom domain, invoked via bun, npx, or npm exec. If none of the three runners is found on PATH it bails before attempting the wrangler call.

Solutions

  1. Install Node.js (provides npm and npx) or bun and ensure it is on PATH
  2. If only pnpm/yarn exist, install npm-compatible tooling or use corepack to provide npx
  3. Fix PATH in the invoking environment (CI env, sudo secure_path, wrapper script)
  4. Pre-flight check: `which bun || which npx || which npm`

Example fix

// before
$ mytool docs hub deploy --domain docs.example.com
Error: bun, npx, or npm is required to run wrangler
// after
$ apt-get install -y nodejs npm   # or install bun
$ mytool docs hub deploy --domain docs.example.com
Defensive patterns

Strategy: validation

Validate before calling

let has_runner = ["bun", "npx", "npm"].iter().any(|c| which::which(c).is_ok());
if !has_runner {
    eprintln!("install Node.js or bun to deploy with wrangler");
    return;
}
deploy_docs_hub(&opts)?;

Try / catch

match deploy_docs_hub(&opts) {
    Err(e) if e.to_string().contains("required to run wrangler") => {
        eprintln!("install bun or Node.js (npm/npx) before deploying with a custom domain");
    }
    Err(e) => return Err(e),
    Ok(()) => {}
}

Prevention

When it happens

Trigger: Calling deploy_docs_hub with a custom domain on a host without bun, npx, or npm; or where Node tooling exists but is not on the PATH of the invoking process.

Common situations: Deploying from CI images without Node; bare servers; PATH stripped under sudo/cron; user only has pnpm/yarn installed (neither bun, npx, nor npm present).

Related errors


AI-assisted analysis of nikivdev/code@a747e741ae (2026-09-01). Data as JSON: /api/errors/a4f96b966c65e859. Report an issue: GitHub.

Appendix: source

Thrown at src/docs.rs:698

fn attach_pages_domain(hub_root: &Path, project: &str, domain: &str) -> Result<()> {
    println!("Attaching custom domain {domain} to {project}...");
    let mut cmd = if which("bun").is_ok() {
        let mut cmd = Command::new("bun");
        cmd.args(["x", "wrangler", "pages", "domain", "add", project, domain]);
        cmd
    } else if which("npx").is_ok() {
        let mut cmd = Command::new("npx");
        cmd.args(["wrangler", "pages", "domain", "add", project, domain]);
        cmd
    } else if which("npm").is_ok() {
        let mut cmd = Command::new("npm");
        cmd.args([
            "exec", "wrangler", "--", "pages", "domain", "add", project, domain,
        ]);
        cmd
    } else {
        bail!("bun, npx, or npm is required to run wrangler");
    };
    let status = cmd
        .current_dir(hub_root)
        .stdin(std::process::Stdio::inherit())
        .stdout(std::process::Stdio::inherit())
        .stderr(std::process::Stdio::inherit())
        .status()
        .context("failed to run wrangler pages domain add")?;
    if !status.success() {
        bail!("wrangler pages domain add failed");
    }
    Ok(())
}

fn prompt_line(message: &str, default: Option<&str>) -> Result<String> {
    if let Some(default) = default {
        print!("{message} [{default}]: ");
    } else {

View on GitHub (pinned to a747e741ae)