{"record":{"id":"d67c1fabf7ffd5d3","repo":"vectordotdev/vector","slug":"invalidinput","errorCode":"InvalidInput","errorMessage":"cannot extend a file through the truncation API","messagePattern":"cannot extend a file through the truncation API","errorType":"exception","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"lib/vector-buffers/src/variants/disk_v2/io.rs","lineNumber":298,"sourceCode":"/// Builds a set of `OpenOptions` for opening a file as readable.\nfn open_readable_file_options() -> OpenOptions {\n    let mut open_options = OpenOptions::new();\n    open_options.read(true);\n    open_options\n}\n\nimpl AsyncFile for tokio::fs::File {\n    async fn metadata(&self) -> io::Result<Metadata> {\n        let metadata = self.metadata().await?;\n        Ok(Metadata {\n            len: metadata.len(),\n        })\n    }\n\n    async fn truncate(&self, size: u64) -> io::Result<()> {\n        let current_size = self.metadata().await?.len();\n        if size > current_size {\n            return Err(io::Error::new(\n                io::ErrorKind::InvalidInput,\n                \"cannot extend a file through the truncation API\",\n            ));\n        }\n\n        self.set_len(size).await\n    }\n\n    async fn sync_all(&self) -> io::Result<()> {\n        self.sync_all().await\n    }\n}\n\nimpl ReadableMemoryMap for memmap2::Mmap {}\n\nimpl ReadableMemoryMap for memmap2::MmapMut {}\n\nimpl WritableMemoryMap for memmap2::MmapMut {","sourceCodeStart":280,"sourceCodeEnd":316,"githubUrl":"https://github.com/vectordotdev/vector/blob/3708c39b12a93212ed8b8d7510b4cc7769cb5864/lib/vector-buffers/src/variants/disk_v2/io.rs#L280-L316","documentation":"The disk-buffer v2 `AsyncFile` trait's `truncate` is intentionally shrink-only: it compares the requested size against the file's current length and returns `InvalidInput` (\"cannot extend a file through the truncation API\") if `size > current_size`, before calling `set_len`. This mirrors `ftruncate(2)` semantics the codebase chose to enforce explicitly, preventing accidental file growth that would corrupt the data-file layout.","triggerScenarios":"Library code (or a custom `AsyncFile` implementation) calling `truncate(n)` where `n` exceeds the file's current length — e.g. computing a truncation offset from a checkpoint that is ahead of the actual file size, or mixing up `truncate` with `resize`/`set_len`.","commonSituations":"Writing a new `AsyncFile`/filesystem backend for disk_v2 (tests, in-memory FS, alternate storage) and reusing the truncation path; checkpoint/replay logic drifting from the physical file length after an external process truncated the data file.","solutions":["Check `metadata().await?.len` first and clamp: only call `truncate(size)` when `size <= current_len`; grow with `set_len` if growth is truly intended.","If the intent is to extend, call `File::set_len` directly instead of the truncation API.","Audit why the requested size exceeds the file — a stale checkpoint or external truncation is usually the real bug."],"exampleFix":"// before\nfile.truncate(size).await?; // size may exceed len → InvalidInput\n\n// after\nlet len = file.metadata().await?.len;\nif size <= len {\n    file.truncate(size).await?;\n} else {\n    file.set_len(size).await?; // explicit growth path\n}","handlingStrategy":"validation","validationCode":"let len = file.metadata().await?.len;\nif size <= len {\n    file.truncate(size).await?;\n} else {\n    return Err(io::Error::new(\n        io::ErrorKind::InvalidInput,\n        format!(\"refusing to grow file (len {len}) to {size} via truncate\"),\n    ));\n}","typeGuard":null,"tryCatchPattern":"match file.truncate(size).await {\n    Ok(()) => {}\n    Err(e) if e.kind() == io::ErrorKind::InvalidInput => {\n        // requested size exceeded current length — treat as stale checkpoint\n        warn!(size, error = %e, \"truncation skipped: would extend file\");\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Treat truncate as shrink-only by convention in all code touching buffer files.","Always derive truncation targets from freshly-read metadata/checkpoints, not cached sizes.","Never let external processes resize buffer data files."],"tags":["disk-buffer","filesystem","truncate","io","vector"],"backgroundTag":"cannot-extend-via-truncate","analyzedSha":"3708c39b12a93212ed8b8d7510b4cc7769cb5864","analyzedAt":"2026-08-20T07:02:18.786Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}