Hmbown/CodeWhale · error · std::io::Error
InvalidInput
InvalidInput
Error message
artifact id and extension must contain safe ASCII characters
What it means
session_artifact_relative_path_with_extension (used by write_session_artifact_bytes for media fetches) sanitizes the artifact id (any char outside [A-Za-z0-9_-] becomes '_') and then requires the sanitized id to be non-empty and the extension — after trimming leading '.' and lowercasing — to be non-empty and purely ASCII alphanumeric. Anything else fails with InvalidInput: notably extensions containing '-', '+', or an inner dot such as 'svg+xml', 'x-tar', or 'tar.gz' are rejected, not normalized.
Source
Thrown at crates/tui/src/artifacts.rs:85
#[must_use]
pub fn session_artifact_relative_path(artifact_id: &str) -> PathBuf {
PathBuf::from(ARTIFACTS_DIR_NAME).join(format!("{artifact_id}.txt"))
}
fn session_artifact_relative_path_with_extension(
artifact_id: &str,
extension: &str,
) -> io::Result<PathBuf> {
let artifact_id = sanitize_id_component(artifact_id);
let extension = extension.trim_start_matches('.').to_ascii_lowercase();
if artifact_id.is_empty()
|| extension.is_empty()
|| !extension
.chars()
.all(|character| character.is_ascii_alphanumeric())
{
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"artifact id and extension must contain safe ASCII characters",
));
}
Ok(PathBuf::from(ARTIFACTS_DIR_NAME).join(format!("{artifact_id}.{extension}")))
}
fn artifact_sessions_root() -> Option<PathBuf> {
#[cfg(test)]
if let Some(root) = TEST_ARTIFACT_SESSIONS_ROOT
.lock()
.unwrap_or_else(|err| err.into_inner())
.clone()
{
return Some(root);
}
// Honor explicit HOME/USERPROFILE isolation before consulting the hostView on GitHub (pinned to 0c42157ee5)
Solutions
- Map MIME types to simple alphanumeric extensions before writing (image/svg+xml -> svg, application/x-tar -> tar, image/jpeg -> jpg)
- Filter the extension: ext.chars().filter(|c| c.is_ascii_alphanumeric()).collect::<String>() and reject if empty
- Build ids via artifact_id_for_tool_call(tool_call_id) so the sanitized id is never empty
- Default a missing extension to a known-good value such as bin
Example fix
// before let (abs, rel) = write_session_artifact_bytes(sid, art_id, "svg+xml", bytes)?; // InvalidInput // after let ext: String = "svg+xml".chars().filter(|c| c.is_ascii_alphanumeric()).take(4).collect::<String>(); // "svgxml" let (abs, rel) = write_session_artifact_bytes(sid, art_id, &ext, bytes)?;
Defensive patterns
Strategy: validation
Validate before calling
fn safe_extension(ext: &str) -> Option<String> {
let ext = ext.trim_start_matches('.').to_ascii_lowercase();
(!ext.is_empty() && ext.chars().all(|c| c.is_ascii_alphanumeric())).then_some(ext)
}
let ext = safe_extension(raw_ext).unwrap_or_else(|| "bin".into()); Type guard
fn is_valid_artifact_extension(ext: &str) -> bool {
let ext = ext.trim_start_matches('.').to_ascii_lowercase();
!ext.is_empty() && ext.chars().all(|c| c.is_ascii_alphanumeric())
} Prevention
- Map MIME types to a fixed extension table instead of passing subtype strings through
- Default unknown/empty extensions to 'bin' rather than failing the whole fetch
- Build artifact ids via artifact_id_for_tool_call so sanitized ids are never empty
When it happens
Trigger: Calling write_session_artifact_bytes(session_id, artifact_id, extension, bytes) with a MIME-derived extension like "svg+xml"/"x-tar"/"tar.gz", an empty extension (URL had no suffix and none was defaulted), or an artifact_id made entirely of unsafe characters that sanitizes to the empty string (e.g. "../.."). Note '.PNG' and 'JPG' are fine (trimmed and lowercased).
Common situations: Deriving the extension from a Content-Type subtype or URL query parameter instead of a mapped table; compound archive extensions; a missing tool_call_id producing an empty artifact id.
Related errors
- API key contains invalid control characters
- The Codewhale service returned an account without an ID
- The Codewhale service returned an invalid user code
- The Codewhale service returned an invalid device authorizati
- API key must be {MIN_API_KEY_BYTES}-{MAX_API_KEY_BYTES} UTF-
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/8b3dbbc3ae879632.
Report an issue: GitHub.