{"record":{"id":"663c0b86f4de8c8b","repo":"tursodatabase/turso","slug":"invaliddata-663c0b","errorCode":"InvalidData","errorMessage":"malformed chunk record","messagePattern":"malformed chunk record","errorType":"exception","errorClass":"std::io::Error","httpStatus":null,"severity":"error","filePath":"core/index_method/fts.rs","lineNumber":868,"sourceCode":"\n            // Extract and validate\n            let found_path = record.get_value_opt(0).and_then(|v| match v {\n                crate::types::ValueRef::Text(t) => Some(t.value.to_string()),\n                _ => None,\n            });\n            let found_chunk = record.get_value_opt(1).and_then(|v| match v {\n                crate::types::ValueRef::Numeric(crate::numeric::Numeric::Integer(i)) => Some(i),\n                _ => None,\n            });\n            let bytes = record.get_value_opt(2).and_then(|v| match v {\n                crate::types::ValueRef::Blob(b) => Some(b.to_vec()),\n                _ => None,\n            });\n\n            let (found_path_str, found_chunk_no, bytes) = match (found_path, found_chunk, bytes) {\n                (Some(p), Some(c), Some(b)) => (p, c, b),\n                _ => {\n                    return Err(std::io::Error::new(\n                        std::io::ErrorKind::InvalidData,\n                        \"malformed chunk record\",\n                    ));\n                }\n            };\n\n            if found_path_str != path_str || found_chunk_no != expected_chunk_no as i64 {\n                return Err(io_not_found(format!(\n                    \"wrong chunk: expected {path_str}:{expected_chunk_no}, got {found_path_str}:{found_chunk_no}\",\n                )));\n            }\n\n            // Cache and collect the chunk\n            if can_cache_chunks(path) {\n                let cache_key = (path.to_path_buf(), expected_chunk_no as i64);\n                self.chunk_cache.put(cache_key, bytes.clone());\n            }\n            chunks.push(Arc::from(bytes));","sourceCodeStart":850,"sourceCodeEnd":886,"githubUrl":"https://github.com/tursodatabase/turso/blob/244cde92a7df7f9b8b8b7a4075c35a12977e303e/core/index_method/fts.rs#L850-L886","documentation":"FTS index chunk records must carry (path: Text, chunk_no: Integer, bytes: Blob). While writing or rotating chunks, the code reads back the current record; if any column is missing or has the wrong type, it refuses with InvalidData \"malformed chunk record\" rather than corrupting the index further.","triggerScenarios":"The FTS index storage no longer matches the expected record layout: shadow tables edited manually, a partially-written record after a crash, or an index built by an incompatible turso version.","commonSituations":"Direct INSERT/UPDATE into FTS shadow tables, a killed process leaving a half-written chunk, or downgrade/upgrade across format changes.","solutions":["Drop and rebuild the FTS index: `INSERT INTO ft(ft) VALUES('rebuild')` or DROP/CREATE the index","If rebuilding fails, restore the database from a known-good backup","Stop touching shadow tables directly - use the fts table's documented controls","If it appeared right after a version change, report it with both turso versions"],"exampleFix":"-- before: index left malformed (e.g. after direct shadow-table edits)\n-- after: rebuild the index from the content table\nINSERT INTO ft(ft) VALUES('rebuild');","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"match write_chunk_result {\n    Err(ref e) if e.kind() == std::io::ErrorKind::InvalidData => rebuild_fts_index(),\n    other => other,\n}","preventionTips":["Never modify FTS shadow tables manually","Rebuild indexes after restores and version upgrades","Keep backups before bulk FTS writes"],"tags":["fts","full-text-search","index","corruption","rust"],"backgroundTag":"index-corruption","analyzedSha":"244cde92a7df7f9b8b8b7a4075c35a12977e303e","analyzedAt":"2026-08-20T07:02:18.389Z","contentChangedAt":"2026-08-20T07:02:18.389Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}