jdx/mise · error

no watch could be installed for the tracked set

Error message

no watch could be installed for the tracked set

What it means

After attempting to install watches for all wanted anchors, the runtime found that zero watches could be installed even though the tracked set is non-empty. Since no event would ever arrive, the caller stops and the service restarts reconciliation instead of running blind until some possibly-disabled reconciliation pass.

Source

Thrown at src/system/history/watch/runtime.rs:1442

            Err(err) => {
                let message = format!(
                    "cannot watch {}: {err}; reconciliation still saves it",
                    display_path(&anchor.path)
                );
                capture.health.watcher.degraded.push(message.clone());
                capture.out.emit(
                    "degraded",
                    &message,
                    json!({ "path": display_path(&anchor.path), "message": err.to_string() }),
                );
            }
        }
    }
    // nothing watched while something should be: no event would ever
    // arrive, so the caller stops (and the service restarts it) instead of
    // running blind until a reconciliation that may be disabled
    if current.is_empty() && !wanted.is_empty() {
        bail!("no watch could be installed for the tracked set");
    }
    capture.anchor_ids = current
        .iter()
        .filter_map(|anchor| {
            file_id::get_file_id(&anchor.path)
                .ok()
                .map(|id| (anchor.path.clone(), id))
        })
        .collect();
    Ok(current)
}

fn anchor_replaced(
    ids: &std::collections::BTreeMap<PathBuf, file_id::FileId>,
    path: &Path,
) -> bool {
    ids.get(path)
        .is_some_and(|before| file_id::get_file_id(path).ok().as_ref() != Some(before))

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Raise the OS watch limit (Linux: fs.inotify.max_user_watches; macOS: check kern.maxfiles / limits).
  2. Verify each tracked root still exists and is readable; untrack roots that were deleted or moved.
  3. Check filesystem support — watches fail on some network filesystems (NFS) or pseudo-filesystems; untrack those roots.
  4. Restart the history service after fixing limits so it retries watch installation.

Example fix

// before: every root unwatchable, `current` empty
// after: shrink tracked set and raise limits, then retry
mise history untrack ~/huge-vendored-tree
sudo sysctl -w fs.inotify.max_user_watches=524288
Defensive patterns

Strategy: retry

Validate before calling

// ensure at least one tracked root is watchable before enabling the watcher
for root in &tracked_roots {
    if !root.exists() || is_unwatchable_fs(root) {
        eprintln!("root {:?} cannot be watched", root);
    }
}

Try / catch

match result {
    Err(e) if e.to_string().contains("no watch could be installed") => {
        eprintln!("retry after raising limits / fixing roots; rely on reconciliation until then");
        schedule_reconciliation_retry();
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling run/reinstall when every debouncer.watch() call for the tracked roots fails (e.g. all roots hit MaxFilesWatch, or roots have disappeared/unreadable), leaving `current` empty while `wanted` is non-empty.

Common situations: System-wide watch exhaustion combined with large tracked sets; tracked directories deleted or permission-restricted after enrollment; containers where inotify is unavailable or its limit is 0.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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