{"record":{"id":"9868e696676d3145","repo":"Hmbown/CodeWhale","slug":"pinned-source-is-not-bounded-text","errorCode":null,"errorMessage":"Pinned source is not bounded text","messagePattern":"Pinned source is not bounded text","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/tools/review_pr.rs","lineNumber":448,"sourceCode":"        .context(\"No pinned source blob\")?;\n    let fields = header.split_whitespace().collect::<Vec<_>>();\n    anyhow::ensure!(\n        returned_path == path\n            && fields.len() == 4\n            && matches!(fields[0], \"100644\" | \"100755\")\n            && fields[1] == \"blob\"\n            && commit_id(fields[2])\n            && fields[3]\n                .parse::<usize>()\n                .is_ok_and(|size| size <= MAX_CONTEXT_FILE_BYTES),\n        \"Pinned source is missing, non-regular or exceeds the context limit\"\n    );\n    let source = run_command(\n        workspace,\n        Program::Git,\n        &[\"cat-file\".into(), \"blob\".into(), fields[2].into()],\n    )?;\n    anyhow::ensure!(\n        source.len() <= MAX_CONTEXT_FILE_BYTES && !source.contains('\\0'),\n        \"Pinned source is not bounded text\"\n    );\n    Ok(source)\n}\n\nfn read_bounded(reader: impl Read, limit: usize) -> std::io::Result<Vec<u8>> {\n    let mut bytes = Vec::new();\n    reader.take(limit as u64 + 1).read_to_end(&mut bytes)?;\n    Ok(bytes)\n}\n\nfn run_command(workspace: &Path, program: Program, args: &[String]) -> Result<String> {\n    let mut command = match program {\n        Program::Gh => Gh::command().context(\"PR review requires GitHub CLI on PATH\")?,\n        Program::Git => Git::review_command(workspace)?,\n    };\n    command","sourceCodeStart":430,"sourceCodeEnd":466,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/tools/review_pr.rs#L430-L466","documentation":"After reading the pinned blob with `git cat-file blob`, `context_blob` enforces two bounds: the content must be at most `MAX_CONTEXT_FILE_BYTES` and must contain no NUL byte. This guarantees the source context handed to review passes is bounded plain text, not a huge or binary payload.","triggerScenarios":"The pinned blob exceeds `MAX_CONTEXT_FILE_BYTES`, or the blob is binary (contains a NUL byte) — e.g. a minified bundle, image, or compiled artifact was committed and included in the review context.","commonSituations":"Reviewing PRs that commit build artifacts, vendor trees, or generated minified JS; large data fixtures; accidentally committed binaries that pass the regular-file check.","solutions":["Exclude large/binary files from review context (respect .gitattributes or a size filter) before calling `source_context`","Add the offending file to .gitignore or remove it from the repository","Increase `MAX_CONTEXT_FILE_BYTES` only if the pipeline can genuinely afford larger contexts"],"exampleFix":"// before\nfor path in changed_files { ctx.push(source_context(ws, commit, path)?); }\n// after\nfor path in changed_files {\n    if is_probably_binary(ws, commit, &path) { continue; }\n    if blob_size(ws, commit, &path)? > MAX_CONTEXT_FILE_BYTES { continue; }\n    ctx.push(source_context(ws, commit, &path)?);\n}","handlingStrategy":"validation","validationCode":"let size = git([\"cat-file\", \"-s\", blob_id])?; if size > MAX_CONTEXT_FILE_BYTES { skip(path); } let head = read_first_bytes(ws, blob_id, 8192)?; if head.contains(&0) { skip_binary(path); }","typeGuard":null,"tryCatchPattern":"match source_context(ws, commit, path) { Ok(src) => Some(src), Err(e) if e.to_string().contains(\"bounded text\") => { warn!(\"skipping oversized/binary {path}\"); None }, Err(e) => return Err(e) }","preventionTips":["Pre-filter review context by blob size and a NUL-byte sniff","Keep build artifacts and binaries out of the repo (.gitignore, CI checks)","Only raise MAX_CONTEXT_FILE_BYTES deliberately, with budget analysis"],"tags":["git","review","size-limit","binary-file"],"backgroundTag":"file-size-limit-exceeded","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}