Hmbown/CodeWhale · error
workspace identity must be a lowercase SHA-256 hash
Error message
workspace identity must be a lowercase SHA-256 hash
What it means
safe_component validates that a value used as a filesystem path component in the memory store is exactly 64 lowercase hex characters, i.e. a lowercase SHA-256 hash. Workspace identities are hashed before being embedded in directory names, so anything else is rejected to prevent path injection and ambiguous layout.
Solutions
- Hash the workspace identity with SHA-256 and lowercase-hex-encode it before passing it in.
- If you already have a hash, lowercase it and verify it is exactly 64 hex characters.
- Use the library's own workspace-scope derivation helper rather than constructing the identifier manually.
Example fix
// before
let scope = workspace_scope("/home/me/project")?; // rejected
// after
let digest = Sha256::digest("/home/me/project");
let scope = workspace_scope(hex::encode(digest))?; // 64 lowercase hex chars Defensive patterns
Strategy: type-guard
Validate before calling
fn is_sha256_lower_hex(s: &str) -> bool {
s.len() == 64 && s.bytes().all(|c| c.is_ascii_hexdigit() && !c.is_ascii_uppercase())
} Type guard
fn valid_workspace_id(id: &str) -> Option<&str> {
(id.len() == 64 && id.bytes().all(|c| c.is_ascii_hexdigit() && !c.is_ascii_uppercase())).then_some(id)
} Prevention
- Always derive workspace identities via SHA-256 lowercase hex, never raw names or paths.
- Keep a helper that hashes workspace identifiers at the boundary.
- Lowercase any digest produced by external tools before use.
- Add a unit check that generated workspace ids are 64 lowercase hex chars.
When it happens
Trigger: Calling workspace_scope (or anything else routing through safe_component) with a raw workspace name/path instead of its 64-char lowercase hex SHA-256, or with an uppercase-hex hash.
Common situations: Passing a project directory path or workspace name directly instead of hashing it; using an uppercase digest from a different hashing tool; truncating or padding the hash.
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
- invalid session id for memory reconcile
- cloud job id must look like cloud_
- correction must match exactly one active note on the…
- DeepSeek Harness credentials line
- Invalid durable task id
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/35000472afa9d053.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/native_memory.rs:620
match store.get(&access, &m.id) {
Ok(current) => {
store.forget(&access, ¤t.id, current.revision)?;
}
Err(codewhale_memory::Error::NotFound) => {}
Err(e) => return Err(e.into()),
}
}
}
Ok(())
}
}
fn safe_component(value: &str) -> Result<()> {
if value.len() != 64
|| !value
.bytes()
.all(|c| c.is_ascii_hexdigit() && !c.is_ascii_uppercase())
{
bail!("workspace identity must be a lowercase SHA-256 hash");
}
Ok(())
}
/// Compose the user-memory prompt block for the native store resolved from a
/// memory path. Single seam used by the engine, the TUI system-prompt
/// builder, and the context report so all three describe the same bytes.
/// Returns `None` when memory is disabled, the path is not a native
/// `memory/global/MEMORY.md` layout, or there is nothing worth injecting.
/// The block is a `codewhale.memory.context.v1` envelope: memory entries are
/// untrusted evidence, never instructions.
#[must_use]
pub fn native_prompt_block(enabled: bool, memory_path: &Path, workspace: &Path) -> Option<String> {
if !enabled {
return None;
}
NativeMemoryStore::from_global_path(memory_path)?
.prompt_block(workspace, 32, 12_000)View on GitHub (pinned to 73e0f67d83)