jdx/mise · error

{rel} is not a directory of the repository

Error message

{rel} is not a directory of the repository

What it means

packslip's fetch_repo_dir downloads a directory listing of a repo path via the GitHub API. It expects the JSON response to be an array of directory entries; if the API returns anything else (e.g. an object describing a single file, or an error payload), it means the requested path is not a directory in that repository at that ref. The bail conveys that the path must point at a directory to fetch its files.

Source

Thrown at src/packslip.rs:483

    commit: &str,
    rel: &str,
    dest: &Path,
    pr: &dyn SingleReport,
) -> Result<()> {
    let url = format!(
        "https://api.github.com/repos/{repo}/contents/{}?ref={commit}",
        url_path(rel)
    );
    // The client asks for the raw media type on every contents URL, which
    // is right for a file body and wrong for a directory listing.
    let mut headers = github::get_headers(&url)?;
    headers.insert(
        reqwest::header::ACCEPT,
        HeaderValue::from_static("application/vnd.github+json"),
    );
    let listing: serde_json::Value = HTTP_FETCH.json_with_headers(&url, &headers).await?;
    let Some(entries) = listing.as_array() else {
        bail!("{rel} is not a directory of the repository");
    };
    file::create_dir_all(dest)?;
    for entry in entries {
        let Some(name) = entry["name"].as_str() else {
            continue;
        };
        if !file::is_plain_file_name(name) {
            continue;
        }
        match entry["type"].as_str() {
            Some("dir") => {
                Box::pin(fetch_repo_dir(
                    repo,
                    commit,
                    &format!("{rel}/{name}"),
                    &dest.join(name),
                    pr,
                ))

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Verify the `rel` path in the manifest is a directory that exists in the repo at the requested ref (check on GitHub).
  2. Confirm the ref/commit/tag being fetched still contains that directory; update to a ref where it exists.
  3. If the target is a single file, use the file-fetch path instead of a directory fetch.
  4. Check for GitHub API error responses (rate limiting, auth) that can replace the array with an error object; set a token if rate limited.

Example fix

// before: manifest points at a single file as a dir
fetch_dir = "src/packslip.rs"

// after
fetch_dir = "src"
Defensive patterns

Strategy: validation

Validate before calling

async fn is_repo_dir(rel: &str, ref_: &str) -> bool {
    // GitHub contents API returns an array for directories, an object for files
    let listing = list_repo_dir(rel, ref_).await.ok();
    matches!(listing, Some(serde_json::Value::Array(_)))
}

Type guard

let Some(entries) = listing.as_array() else { return Err(...); };

Try / catch

match fetch_repo_dir(rel, ref).await {
    Err(e) if e.to_string().contains("is not a directory") => eprintln!("{rel} is not a dir at {ref}; check the manifest path"),
    Ok(entries) => use_entries(entries),
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling fetch_repo_dir (directly or via fetch_files) with a `rel` path that is a single file, a nonexistent path, or a repo/ref that returns a non-array listing from the GitHub contents endpoint.

Common situations: A packslip manifest points at a file instead of a directory; the file was renamed or deleted upstream so the path 404s and the API returns an error object instead of an array; the ref (tag/commit) no longer contains the directory.

Related errors


AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/9806623164c39aba. Report an issue: GitHub.