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

  1. 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.
  2. 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`.
  3. 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`.
  4. For vendored/custom grammars, regenerate them with `tree-sitter generate` using the CLI version matching the 0.26.10 runtime and rebuild.
  5. 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

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.