Hmbown/CodeWhale · error · anyhow::Error
Invalid MCP consent history; contents omitted
Error message
Invalid MCP consent history; contents omitted
What it means
persist_decisions writes import-consent decisions into the MCP config file under a process-wide write lock. Before mutating, it re-parses the existing file; if the file exists but is not valid JSON, the write aborts rather than silently replacing the user's malformed consent history with empty state — contents are deliberately not echoed.
Solutions
- Open the config file, fix or remove the invalid JSON, and retry the import flow
- If the history is unrecoverable, back up the file, delete it, and let the tool recreate it (prior consent decisions are lost)
- Restore the file from version control or backup before re-running the consent flow
Defensive patterns
Strategy: try-catch
Validate before calling
const raw = fs.readFileSync(path, "utf8");
try { JSON.parse(raw); } catch (e) { /* repair or restore the file before proceeding */ } Try / catch
catch the error, then attempt JSON.parse yourself to locate the corruption; repair or restore from backup before retrying the consent flow
Prevention
- Let the tool perform all writes to the consent/config file (atomic writes); never hand-edit mid-flow
- Keep the config under version control so corrupted files can be restored
- Watch for merge-conflict markers in config files after git merges
When it happens
Trigger: `persist_decisions` (via apply_reviewed_import or the consent-transaction test) is called on a path whose config file exists but contains corrupted/partial JSON — a truncated write, manual edit error, or leftover merge-conflict markers.
Common situations: A crashed earlier write left a half-written file; a git merge of the config left `<<<<<<<` markers; the file was saved with a non-JSON encoding or stray BOM.
Understand the failure class
Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.
Related errors
- MCP consent history must be an object
- app_denied
- consent_required
- .annotations is not a valid MCP annotations object
- .size must be an integer
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/2a24d4d31df7649d.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/mcp/external_import.rs:297
fn hex_sha256(bytes: &[u8]) -> String {
let digest = Sha256::digest(bytes);
digest.iter().map(|b| format!("{b:02x}")).collect()
}
/// Record decisions against the latest consent document under the shared
/// process lock. Malformed history is never silently replaced with empty state.
pub fn persist_decisions(
path: &Path,
candidates: &[ImportCandidate],
decisions: &HashMap<String, ImportDecision>,
now_unix: u64,
) -> anyhow::Result<()> {
super::validate_mcp_config_path(path)?;
codewhale_config::with_config_write_lock(path, |path| {
let original = super::read_mcp_config_file(path)?;
let mut raw: Value = match original.as_deref() {
Some(raw) => serde_json::from_str(raw)
.map_err(|_| anyhow::anyhow!("Invalid MCP consent history; contents omitted"))?,
None => serde_json::json!({}),
};
anyhow::ensure!(raw.is_object(), "MCP consent history must be an object");
let mut store: ImportConsentStore = if original.is_none() {
ImportConsentStore::default()
} else {
serde_json::from_value(raw.clone())
.map_err(|_| anyhow::anyhow!("Invalid MCP consent history; contents omitted"))?
};
let before = serde_json::to_value(&store)?;
record_decisions(&mut store, candidates, decisions, now_unix);
let after = serde_json::to_value(&store)?;
super::apply_json_delta(&mut raw, &before, &after);
let rendered = serde_json::to_vec_pretty(&raw)?;
if rendered.len() as u64 > super::MAX_MCP_CONFIG_BYTES {
anyhow::bail!("MCP consent history exceeds size limit");
}
crate::utils::write_atomic(path, &rendered)?;View on GitHub (pinned to 73e0f67d83)