jj-vcs/jj · error

cannot store git submodules

Error message

cannot store git submodules

What it means

tree_value_to_proto in lib/src/simple_backend.rs converts a jj TreeValue enum into the protobuf representation used by the simple (native jj) store. The proto schema has variants for File, Executable, Symlink, Tree, and Conflict — but no representation for GitSubmodule. Since git submodules (gitlink entries, mode 160000) cannot be expressed in the simple backend's on-disk format, attempting to serialize a tree entry containing one panics with 'cannot store git submodules'.

Source

Thrown at lib/src/simple_backend.rs:450

            id,
            executable,
            copy_id,
        } => {
            proto.value = Some(crate::protos::simple_store::tree_value::Value::File(
                crate::protos::simple_store::tree_value::File {
                    id: id.to_bytes(),
                    executable: *executable,
                    copy_id: copy_id.to_bytes(),
                },
            ));
        }
        TreeValue::Symlink(id) => {
            proto.value = Some(crate::protos::simple_store::tree_value::Value::SymlinkId(
                id.to_bytes(),
            ));
        }
        TreeValue::GitSubmodule(_id) => {
            panic!("cannot store git submodules");
        }
        TreeValue::Tree(id) => {
            proto.value = Some(crate::protos::simple_store::tree_value::Value::TreeId(
                id.to_bytes(),
            ));
        }
    }
    proto
}

fn tree_value_from_proto(proto: crate::protos::simple_store::TreeValue) -> TreeValue {
    match proto.value.unwrap() {
        crate::protos::simple_store::tree_value::Value::TreeId(id) => {
            TreeValue::Tree(TreeId::new(id))
        }
        crate::protos::simple_store::tree_value::Value::File(
            crate::protos::simple_store::tree_value::File {
                id,

View on GitHub (pinned to 6631dbd4a8)

Solutions

  1. Use a git-backed store/backend for the repo (jj git init / colocated repo) instead of the simple backend, since the simple store format has no submodule representation.
  2. Strip or replace the GitSubmodule tree entries (e.g. by removing the submodule or converting the gitlink to a regular file/placeholder) before serializing the tree.
  3. If you control the import, skip or warn on TreeValue::GitSubmodule entries instead of passing them to the simple backend.
  4. Track/upgrade to a jj version with explicit submodule handling if one becomes available.

Example fix

// before
let proto = tree_value_to_proto(value); // value is TreeValue::GitSubmodule -> panic

// after
match value {
    TreeValue::GitSubmodule(_) => {
        // skip or substitute: simple backend cannot represent gitlinks
        return None;
    }
    other => Some(tree_value_to_proto(other)),
}
Defensive patterns

Strategy: type-guard

Validate before calling

// Before serializing a tree with the simple backend, scan for submodule entries:
fn has_git_submodule(tree: &Tree) -> bool {
    tree.entries().any(|e| matches!(e.value(), TreeValue::GitSubmodule(_)))
}
if has_git_submodule(&tree) { /* refuse or strip before tree_value_to_proto */ }

Type guard

fn is_simple_backend_storable(value: &TreeValue) -> bool {
    !matches!(value, TreeValue::GitSubmodule(_))
}

Try / catch

// Panic aborts; use catch_unwind only to convert to a clean error at the FFI/CLI boundary:
match std::panic::catch_unwind(|| tree_value_to_proto(value)) {
    Ok(proto) => proto,
    Err(_) => return Err(anyhow!("tree contains a git submodule; use the git backend")),
}

Prevention

When it happens

Trigger: Calling tree_value_to_proto (directly or via writing/serializing a tree Entry through the simple store backend, e.g. simple_store::tree::Entry construction or committing a tree) when a TreeValue::GitSubmodule is present in the tree — typically after importing a git commit that contains a submodule into a jj repo backed by the simple backend.

Common situations: Cloning or importing a Git repository that uses submodules into a jj repo without using the git-backed store; jj colocated vs non-colocated setups where the simple backend is the default; tooling that walks and re-serializes trees hitting an imported gitlink entry.

Related errors


AI-assisted analysis of jj-vcs/jj@6631dbd4a8 (2026-08-28). Data as JSON: /api/errors/4fccb2d9a0f07e0b. Report an issue: GitHub.