Hmbown/CodeWhale · error · std::io::Error

NotFound

NotFound

Error message

gh not found

What it means

The /project share flow writes an HTML session export to a temp file and uploads it as a public GitHub Gist via the `gh` CLI. Gh::command() resolves gh on PATH by probing candidates with --version (cached per process); when it returns None, upload_gist maps that to this NotFound error. The gist upload is therefore entirely dependent on gh being installed, authenticated, and visible to the codewhale process.

Source

Thrown at crates/tui/src/commands/groups/project/share.rs:164

}

/// Write HTML to a secure temp file and keep it alive for upload.
fn write_temp_html(html: &str) -> Result<tempfile::NamedTempFile, String> {
    let mut tmp = tempfile::Builder::new()
        .prefix("codewhale-share-")
        .suffix(".html")
        .tempfile()
        .map_err(|e| format!("{e}"))?;
    tmp.write_all(html.as_bytes()).map_err(|e| format!("{e}"))?;
    Ok(tmp)
}

/// Upload a file as a GitHub Gist using the `gh` CLI.
async fn upload_gist(path: &Path) -> Result<String, String> {
    let path_owned = path.to_path_buf();
    let output = tokio::task::spawn_blocking(move || {
        let mut cmd = crate::dependencies::Gh::command()
            .ok_or_else(|| std::io::Error::new(std::io::ErrorKind::NotFound, "gh not found"))?;
        cmd.args([
            "gist",
            "create",
            "--public",
            path_owned.to_string_lossy().as_ref(),
            "--filename",
            "session-export.html",
            "--desc",
            "codewhale Session Export",
        ])
        .output()
    })
    .await
    .map_err(|join_err| format!("gh gist create panicked: {join_err}"))?
    .map_err(|e| format!("Failed to run `gh gist create`: {e}"))?;

    if !output.status.success() {
        let stderr = String::from_utf8_lossy(&output.stderr);

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Install GitHub CLI: brew install gh, winget install GitHub.cli, or see cli.github.com
  2. Confirm `gh --version` and `gh auth login` succeed in the same environment that launches codewhale, then restart the TUI (probe result is cached per process)
  3. Fix PATH so the gh binary's directory is included for the launching process, not only your interactive shell

Example fix

# before
codewhale /project share   # NotFound: gh not found

# after
brew install gh && gh auth login
exec $SHELL   # restart so the probe re-runs
codewhale /project share
Defensive patterns

Strategy: validation

Validate before calling

if !crate::dependencies::Gh::available() {
    return Err("GitHub CLI (gh) is required to share: install from cli.github.com and run gh auth login".into());
}

Type guard

fn share_available() -> bool {
    crate::dependencies::Gh::command().is_some()
}

Prevention

When it happens

Trigger: Running the share/upload command on a machine without GitHub CLI installed, with gh present but not on the PATH of the codewhale process (launched from a GUI, service, or container), or with a broken gh install whose --version probe fails so resolution caches None for the process lifetime.

Common situations: Fresh machines; gh installed via brew/winget but the shell or TUI was not restarted; Windows gh under %LOCALAPPDATA%\GitHub CLI missing from system PATH; minimal dev containers without gh.

Understand the failure class

Background: "Not Found" / HTTP 404 Errors: What They Mean and How to Fix Them Across Libraries — this error's family across 6 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/767a91c2b51344b9. Report an issue: GitHub.