Hmbown/CodeWhale · error · io::Error

invalid session artifact path

Error message

invalid session artifact path

What it means

`write_session_relative_immutable` resolves the absolute path for a session-relative artifact path; if resolution fails because the home directory is missing (or the relative path cannot be anchored), it throws this InvalidInput error before opening the destination handle. Immutable artifacts must never overwrite differing content, so the write aborts early rather than guessing a location.

Solutions

  1. Set HOME (or run as a user with a resolvable home) before the process starts.
  2. Build relative paths with the artifact module's own helpers instead of constructing Paths manually.
  3. Verify the relative path contains no absolute or parent components that would break resolution.
  4. In services, pre-create and configure a home directory for the daemon user.

Example fix

// before (container)
CMD ["codewhale","export"]

// after
ENV HOME=/home/app
RUN mkdir -p /home/app && chown -R app:app /home/app
CMD ["codewhale","export"]
Defensive patterns

Strategy: validation

Validate before calling

fn immut_write_ready(home_set: bool, rel: &Path) -> bool {
    home_set && rel.is_relative() && !rel.components().any(|c| matches!(c, std::path::Component::ParentDir))
}

Try / catch

match write_session_relative_immutable(session, rel, bytes) {
    Err(e) if e.kind() == std::io::ErrorKind::InvalidInput && e.to_string().contains("invalid session artifact path") => {
        // HOME missing or bad relative path: fix env/path, retry
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling `write_session_relative_immutable` (directly or via `write_session_artifact_immutable`, evidence publication, or `export`) in an environment where `session_artifact_absolute_path` returns None — typically HOME unresolvable — or with a relative_path the resolver rejects.

Common situations: Headless service/cron/container runs without HOME set; exporting artifacts after switching users; passing a relative path built outside the artifacts API (e.g. absolute or '..'-containing) that cannot be anchored.

Understand the failure class

Background: "environment variable is not set" and "Missing keys in environment" errors: what missing required env var messages mean and how to fix them — this error's family across 28 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/2a63e9474b31254c. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/artifacts.rs:164

        })?;
    if let Some(parent) = absolute_path.parent() {
        std::fs::create_dir_all(parent)?;
    }
    crate::utils::write_atomic(&absolute_path, content.as_bytes())?;
    Ok((absolute_path, relative_path))
}

/// Publish immutable session-owned bytes without replacing an earlier handle.
/// A duplicate replay with identical bytes is idempotent; a different payload
/// for the same relative path fails closed.
pub fn write_session_relative_immutable(
    session_id: &str,
    relative_path: &Path,
    content: &[u8],
) -> io::Result<PathBuf> {
    let absolute_path =
        session_artifact_absolute_path(session_id, relative_path).ok_or_else(|| {
            io::Error::new(io::ErrorKind::InvalidInput, "invalid session artifact path")
        })?;
    let destination = open_session_relative(session_id, relative_path, true)?;
    match destination.publish(content) {
        Ok(()) => {}
        Err(err) if err.kind() == io::ErrorKind::AlreadyExists => {
            use std::io::Read;
            let mut existing = Vec::new();
            destination
                .open_file()?
                .take(content.len() as u64 + 1)
                .read_to_end(&mut existing)?;
            if existing != content {
                return Err(io::Error::new(
                    io::ErrorKind::AlreadyExists,
                    "immutable artifact handle already contains different bytes",
                ));
            }
        }

View on GitHub (pinned to 73e0f67d83)