{"record":{"id":"eade7c30a2f96af8","repo":"sinelaw/fresh","slug":"read-range-expected-bytes-at-offset-got-agent-reported-path","errorCode":null,"errorMessage":"read_range: expected {} bytes at offset {}, got {} (agent reported: {:?}, path: {})","messagePattern":"read_range: expected (.+?) bytes at offset (.+?), got (.+?) \\(agent reported: (.+?), path: (.+?)\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/fresh-editor/src/services/remote/filesystem.rs","lineNumber":277,"sourceCode":"        for chunk in data_chunks {\n            if let Some(b64) = chunk.get(\"data\").and_then(|v| v.as_str()) {\n                let decoded = decode_base64(b64)\n                    .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;\n                content.extend(decoded);\n            }\n        }\n\n        // Get the size reported by the agent (how many bytes it actually read from the file)\n        let agent_reported_size = result\n            .get(\"size\")\n            .and_then(|v| v.as_u64())\n            .map(|s| s as usize);\n\n        // Validate that we received the expected number of bytes.\n        // This matches LocalFileSystem::read_range which uses read_exact.\n        // Short reads indicate file truncation, race conditions, or metadata mismatch.\n        if content.len() != len {\n            return Err(io::Error::new(\n                io::ErrorKind::UnexpectedEof,\n                format!(\n                    \"read_range: expected {} bytes at offset {}, got {} (agent reported: {:?}, path: {})\",\n                    len,\n                    offset,\n                    content.len(),\n                    agent_reported_size,\n                    path_str\n                ),\n            ));\n        }\n\n        Ok(content)\n    }\n\n    fn count_line_feeds_in_range(&self, path: &Path, offset: u64, len: usize) -> io::Result<usize> {\n        let path_str = path.to_string_lossy();\n        let result = self","sourceCodeStart":259,"sourceCodeEnd":295,"githubUrl":"https://github.com/sinelaw/fresh/blob/67894ca5463dbd7a89bb31add4627c27d6b79d83/crates/fresh-editor/src/services/remote/filesystem.rs#L259-L295","documentation":"The remote filesystem adapter's read_range requested `len` bytes at `offset` from the remote agent, but the response payload contained a different number of bytes (short or long read). Mirrors LocalFileSystem::read_exact semantics: short reads indicate truncation, races, or stale metadata, so the mismatch is surfaced with the agent-reported length and path for diagnosis.","triggerScenarios":"read_range called on a remote file that shrank (truncate/rewrite) between stat and read, a stale cached file size from the agent, or the remote agent returning fewer/more bytes than requested for the given range.","commonSituations":"Another process appending or rewriting the file during editing, remote file mounted over an unreliable network share, or an agent/protocol version that reports sizes differently (stale metadata cache).","solutions":["Re-stat the file on the remote and retry read_range with fresh metadata (invalidate the cached size).","Check whether the file is being modified concurrently; re-open the file handle or re-read the whole file.","Verify remote agent and editor protocol versions match; upgrade the agent if range semantics differ."],"exampleFix":"// before\nlet bytes = remote_fs.read_range(&path, offset, len)?;\n// after\nlet bytes = match remote_fs.read_range(&path, offset, len) {\n    Ok(b) => b,\n    Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => {\n        remote_fs.refresh_metadata(&path)?; // re-stat, drop cached size\n        remote_fs.read_range(&path, offset, remote_fs.metadata(&path)?.len())?\n    }\n    Err(e) => return Err(e),\n};","handlingStrategy":"retry","validationCode":"let stat_len = remote_fs.metadata(&path)?.len();\nif offset + len as u64 > stat_len { return Err(\"range exceeds current file size\".into()); }","typeGuard":"fn range_in_bounds(meta_len: u64, offset: u64, len: usize) -> bool { offset.saturating_add(len as u64) <= meta_len }","tryCatchPattern":"let bytes = loop {\n    match remote_fs.read_range(&path, offset, len) {\n        Ok(b) => break b,\n        Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => {\n            remote_fs.refresh_metadata(&path)?;\n            remote_fs.read_range(&path, offset, len)\n        }\n        Err(e) => break Err(e),\n    }\n};","preventionTips":["Re-stat remote files before ranged reads; never trust cached sizes across long intervals","Detect concurrent modification (mtime/size change) and re-open the file","Keep remote agent and editor protocol versions in sync"],"tags":["remote","filesystem","short-read","io"],"backgroundTag":"unexpected-response-shape","analyzedSha":"67894ca5463dbd7a89bb31add4627c27d6b79d83","analyzedAt":"2026-09-13T15:04:03.701Z","contentChangedAt":"2026-09-13T15:04:03.701Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}