jdx/mise · error

dotfiles: {} is still tracked

Error message

dotfiles: {} is still tracked

What it means

After attempting to remove tracking (rewinding config and reloading TrackedSet), untrack verifies the path is genuinely gone from the tracked set. If an entry still exists for the exact path, the untrack did not take effect and the command fails rather than leaving inconsistent state.

Source

Thrown at src/cli/dotfiles/untrack.rs:169

                table.insert("enabled", Value::Boolean(toml_edit::Formatted::new(false)));
                doc["dotfiles"][&key] = Item::Value(Value::InlineTable(table));
                file::write(&local, doc.to_string())?;
                info!(
                    "dotfiles: {key} is also declared in {}; switched off in {}",
                    entry
                        .declared_in
                        .as_deref()
                        .map(display_path)
                        .unwrap_or_else(|| "another layer".into()),
                    display_path(&local)
                );
                config = Config::reset().await?;
                tracked = TrackedSet::from_config(&config)?;
                if tracked
                    .entry_for(&path)
                    .is_some_and(|entry| entry.path == path)
                {
                    bail!("dotfiles: {} is still tracked", display_path(&path));
                }
            }
        }
        // A capture command may invoke untrack itself. Its parent holds the
        // operation lock and records the new enrollment in its outcome; do
        // not wait on that parent or commit a competing boundary here.
        if std::env::var_os(crate::system::history::scope::ENV_VAR).is_some()
            || crate::system::history::scope::is_active()
        {
            info!(
                "dotfiles: tracking stopped; the enclosing operation will record the change. Live files and earlier committed versions remain"
            );
            return Ok(());
        }
        let store = crate::system::history::checkpoint::Store::open()?;
        let mut draft = crate::system::history::checkpoint::Draft::new(
            crate::system::history::store::Trigger::Save,
        );

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Re-run `mise dotfiles untrack` (transient reload race resolves itself)
  2. Inspect all config layers (`~/.config/mise/config.toml`, project config, config.local.toml) for a duplicate declaration of the target and remove the extra one
  3. Check for other running mise dotfiles processes that may re-enroll the path
Defensive patterns

Strategy: try-catch

Validate before calling

# no tracked entry should exist for the path before untrack
! mise dotfiles ls | grep -Fx -- "$path" || echo "already declared elsewhere"

Try / catch

mise dotfiles untrack "$path" || {
  sleep 1
  mise dotfiles untrack "$path"   # retry once; config reload race
}

Prevention

When it happens

Trigger: `mise dotfiles untrack` re-reads config (Config::reset) and finds entry_for(path) still matching the exact path — the removal from config did not persist or a parent/sibling declaration still tracks the exact path.

Common situations: Concurrent dotfiles command rewrote the config; the path was declared in multiple config layers (local + global); a capture command re-enrolled it during the operation.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/7140d021071bbc52. Report an issue: GitHub.