Wilfred/difftastic · error
Incompatible tree-sitter version
Error message
Incompatible tree-sitter version
What it means
tree_sitter::Parser::set_language returns Err(LanguageError) when the grammar's compiled ABI version (the LANGUAGE_VERSION constant baked into the grammar crate's generated parser.c) is newer than the tree-sitter runtime supports, or older than MIN_COMPATIBLE_LANGUAGE_VERSION. difftastic calls .expect("Incompatible tree-sitter version") in to_tree (src/parse/tree_sitter_parser.rs:1301), so instead of a Result the process panics the first time it tries to parse a file of that language. The root cause is always a version skew between the `tree-sitter` runtime crate and one of the `tree-sitter-*` grammar crates resolved in Cargo.lock (this repo pins `tree-sitter = "0.26.10"` precisely because newer runtimes broke tree-sitter-solidity).
Source
Thrown at src/parse/tree_sitter_parser.rs:1301
atom_nodes: ["string"].into_iter().collect(),
delimiter_tokens: vec![("{", "}"), ("[", "]"), ("(", ")")]
.into_iter()
.collect(),
ignore_trailing_tokens: vec![],
highlight_query: ts::Query::new(&language, tree_sitter_zig::HIGHLIGHTS_QUERY)
.unwrap(),
sub_languages: vec![],
}
}
}
}
/// Parse `src` with tree-sitter.
pub(crate) fn to_tree(src: &str, config: &TreeSitterConfig) -> tree_sitter::Tree {
let mut parser = ts::Parser::new();
parser
.set_language(&config.language)
.expect("Incompatible tree-sitter version");
parser.parse(src, None).unwrap()
}
#[derive(Debug)]
pub(crate) struct ExceededByteLimit(pub(crate) usize);
pub(crate) fn to_tree_with_limit(
diff_options: &DiffOptions,
config: &TreeSitterConfig,
lhs_src: &str,
rhs_src: &str,
) -> Result<(tree_sitter::Tree, tree_sitter::Tree), ExceededByteLimit> {
if lhs_src.len() > diff_options.byte_limit || rhs_src.len() > diff_options.byte_limit {
let num_bytes = std::cmp::max(lhs_src.len(), rhs_src.len());
return Err(ExceededByteLimit(num_bytes));
}
View on GitHub (pinned to d8fe43b3ef)
Solutions
- Realign the dependency pair in Cargo.lock: run `cargo update -p tree-sitter --precise 0.26.10` (or bump the `tree-sitter = "0.26.10"` requirement to the version the grammar crates were generated for), then rebuild.
- If one grammar crate floated too far ahead, pin it back to the version difftastic's Cargo.toml expects, e.g. `cargo update -p tree-sitter-javascript --precise 0.25.0`.
- Inspect the resolved graph with `cargo tree -i tree-sitter` to find which grammar pulls an incompatible ABI, and check the grammar's `#define LANGUAGE_VERSION` in its generated parser.c against `tree_sitter::LANGUAGE_VERSION` and `MIN_COMPATIBLE_LANGUAGE_VERSION`.
- For vendored/custom grammars, regenerate them with `tree-sitter generate` using the CLI version matching the 0.26.10 runtime and rebuild.
- If you are using difftastic as a binary (not embedding it), upgrade to the latest release, which ships a Cargo.lock with a tested, consistent set.
Example fix
# before (Cargo.toml / Cargo.lock out of sync — runtime too old for grammar ABI) tree-sitter = "0.25.0" tree-sitter-javascript = "0.25.0" # grammar generated with newer CLI, ABI too new for 0.25 runtime # after (runtime supports the grammar's ABI version) tree-sitter = "0.26.10" tree-sitter-javascript = "0.25.0"
Defensive patterns
Strategy: validation
Validate before calling
use tree_sitter::{Language, LANGUAGE_VERSION, MIN_COMPATIBLE_LANGUAGE_VERSION};
fn language_loads(lang: &Language) -> bool {
// tree-sitter >= 0.22 exposes lang.version(); older runtimes: tree_sitter::language_version(lang)
let abi = lang.version();
(MIN_COMPATIBLE_LANGUAGE_VERSION..=LANGUAGE_VERSION).contains(&abi)
}
// run before calling to_tree / difftastic's parse entry points
for config in all_language_configs() {
assert!(
language_loads(&config.language),
"grammar ABI {} outside supported range {}..={}",
config.language.version(),
MIN_COMPATIBLE_LANGUAGE_VERSION,
LANGUAGE_VERSION
);
} Type guard
fn is_abi_compatible(lang: &tree_sitter::Language) -> bool {
(tree_sitter::MIN_COMPATIBLE_LANGUAGE_VERSION..=tree_sitter::LANGUAGE_VERSION)
.contains(&lang.version())
} Try / catch
// set_language failure surfaces as a panic via .expect, not a Result;
// pre-validate instead. As a last-resort containment when embedding difftastic:
let parsed = std::panic::catch_unwind(|| to_tree(src, config));
match parsed {
Ok(tree) => tree,
Err(payload) => {
let msg = payload
.downcast_ref::<&str>()
.copied()
.or_else(|| payload.downcast_ref::<String>().map(|s| s.as_str()));
if msg == Some("Incompatible tree-sitter version") {
// fall back to a text diff / skip this language, and log the ABI mismatch
} else {
std::panic::resume_unwind(payload);
}
}
} Prevention
- Commit Cargo.lock and never run blanket `cargo update` on the tree-sitter dependency cluster; update runtime and grammar crates together in one reviewed change.
- Add a startup check that walks every language config and asserts lang.version() is within MIN_COMPATIBLE_LANGUAGE_VERSION..=LANGUAGE_VERSION before any parsing.
- Keep a CI smoke test that parses one sample file per supported language so ABI breaks fail the build, not users.
- Run `cargo tree -i tree-sitter` after any dependency change to confirm a single consistent runtime version across all grammar crates.
- Regenerate vendored grammars with the same tree-sitter CLI version as the runtime crate, and record that version in the repo.
When it happens
Trigger: Calling to_tree(src, config) after difftastic builds a TreeSitterConfig via from_language: ts::Parser::new() then parser.set_language(&config.language) fails when the resolved grammar crate (e.g. tree-sitter-javascript 0.25.0) was generated with a `tree-sitter generate` CLI whose ABI exceeds what the resolved runtime tree-sitter 0.26.10 can load. Concretely: a `cargo update` that advances a grammar crate past the pinned runtime (or downgrades the runtime), or a vendored/forked grammar regenerated with a different tree-sitter CLI.
Common situations: Running `cargo update` in difftastic or a project embedding it, which moves grammar crates and runtime out of lockstep. Pinning an older `tree-sitter` runtime while grammar crates drift newer. Using a forked grammar crate rebuilt with the latest tree-sitter CLI. Major tree-sitter releases that bump LANGUAGE_VERSION (e.g. the ABI 14-to-15 transitions) make previously working pairs fail. Installing a difftastic binary built against mismatched dependency versions.
AI-assisted analysis of Wilfred/difftastic@d8fe43b3ef (2026-08-16).
Data as JSON: /api/errors/6580cda51f4b296c.
Report an issue: GitHub.