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
- Set HOME (or run as a user with a resolvable home) before the process starts.
- Build relative paths with the artifact module's own helpers instead of constructing Paths manually.
- Verify the relative path contains no absolute or parent components that would break resolution.
- 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
- Ensure HOME is set before running artifact exports in services.
- Build relative paths only with the artifacts module helpers.
- Reject absolute or '..'-containing paths before calling.
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
- could not resolve session artifact path (missing home…
- AlreadyExists
- An existing Fleet artifact contains different bytes
- CODEWHALE_HOME / user home is unavailable
- could not resolve home directory for FileKeyringStore
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)