Hmbown/CodeWhale · error · std::io::Error

legacy spillover ownership requires a session id

Error message

legacy spillover ownership requires a session id

What it means

InvalidInput returned by publish_legacy_spillover_ownership when the session id passed in is empty or whitespace-only. The function writes the ownership sidecar that makes a legacy-global spillover payload retrievable; without a real session id the ownership record would be meaningless, so publication is refused before any bytes are written (the payload is deliberately left unowned and callers must not advertise a retrieval hint).

Source

Thrown at crates/tui/src/tools/truncate.rs:140

}

#[must_use]
pub(crate) fn legacy_spillover_ownership_path(payload_path: &Path) -> PathBuf {
    payload_path.with_extension("owner.json")
}

/// Publish the proof needed to retrieve a legacy-global spillover safely.
///
/// Payload publication happens first. If this atomic sidecar write fails, the
/// payload is deliberately left unowned and therefore inaccessible through
/// `retrieve_tool_result`; callers must not advertise a retrieval hint.
pub(crate) fn publish_legacy_spillover_ownership(
    payload_path: &Path,
    session_id: &str,
    bytes: &[u8],
) -> io::Result<PathBuf> {
    if session_id.trim().is_empty() {
        return Err(io::Error::new(
            io::ErrorKind::InvalidInput,
            "legacy spillover ownership requires a session id",
        ));
    }
    let ownership = LegacySpilloverOwnership {
        schema_version: LEGACY_SPILLOVER_OWNER_SCHEMA_VERSION,
        origin_session: session_id.to_string(),
        digest: crate::hashing::sha256_hex(bytes),
        size_bytes: bytes.len().try_into().unwrap_or(u64::MAX),
    };
    let sidecar = legacy_spillover_ownership_path(payload_path);
    let encoded = serde_json::to_vec_pretty(&ownership)
        .map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))?;
    crate::utils::write_atomic(&sidecar, &encoded)?;
    Ok(sidecar)
}

pub(crate) fn read_legacy_spillover_ownership(

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Establish (or load) the session first and pass its non-empty id before large output can spill over
  2. Guard at the call site: if session_id.trim().is_empty(), keep output inline instead of publishing spillover
  3. Audit callers added during refactors — this contract is easy to miss because the signature takes &str

Example fix

// before
publish_legacy_spillover_ownership(&path, "", &bytes)?; // InvalidInput

// after
if let Some(sid) = session_id.filter(|s| !s.trim().is_empty()) {
    publish_legacy_spillover_ownership(&path, sid, &bytes)?;
} else {
    // keep output inline; do not advertise a retrieval hint
}
Defensive patterns

Strategy: validation

Validate before calling

fn session_id_usable(id: &str) -> bool { !id.trim().is_empty() }

if !session_id_usable(&session_id) {
    // keep output inline; do not call publish_legacy_spillover_ownership
}

Type guard

fn is_missing_session_id(e: &std::io::Error) -> bool {
    e.kind() == std::io::ErrorKind::InvalidInput
        && e.to_string().contains("requires a session id")
}

Prevention

When it happens

Trigger: Large tool output routed to the legacy-global spillover path before a session context exists — e.g. a headless/embedded run or early startup call where the caller passes "" or a blank session id into publish_legacy_spillover_ownership.

Common situations: New integration that streams tool output before creating/opening a session; refactoring that reordered session initialization; tests that exercise the truncate path without a session fixture; whitespace sneaking in through config-provided session labels.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/308d2aab34f522bf. Report an issue: GitHub.