Troubleshooting

Common Issues

"Operation not permitted" after you granted the path

A filesystem entitlement only reaches the kernel if its path exists when the agent starts. Granting a directory you hadn't created yet used to be accepted by the CLI and then dropped when the sandbox profile was sealed — perm said OK, restart said applied, and the write still failed with a bare errno.

mur agent perm allow-read / allow-write now refuse a path that doesn't exist and print the mkdir -p to run. If a dropped grant is already sitting in a profile, mur agent runtime-doctor names it and the fix:

mur agent runtime-doctor        # names grants with no effect, and why
mkdir -p <path>                 # create every granted path that is missing
mur agent restart <agent>

Order matters: create the directory before restarting. Restarting first seals the profile without the grant, and the symptom is identical.

Your agent answers by repeating you back

An agent whose model reference does not resolve, or whose provider client will not build, used to start anyway and reply as an echo stub — alive, responsive, and parroting. It says so now:

āš ļø This agent has no working model, so it cannot answer.
Reason: model_ref "chatgpt_gpt_5_4_min" not in the registry
Diagnose: `mur agent doctor pm`
Pick a model: `mur model list`, then `/model` in this chat
Apply it: `mur agent restart pm`

Only a deliberate provider: echo still echoes. If you see a parrot with no notice, the agent is older than this change — restart it against a current runtime.

"Operation not permitted" on a binary your profile allows

A spawn allowlist is resolved by scanning the executable directories when the agent starts; the exec that follows resolves the same name through PATH minutes or hours later. Install a second copy of that binary into a higher-priority directory in between — a brew upgrade alongside a ~/.local/bin build, say — and the agent execs a file it never allowlisted, under a profile that plainly names the binary.

mur agent restart <agent> re-scans and covers both copies. Widening the profile does not help: the new entry is a name, resolved the same way at the next seal, so the same race produces the same denial.

MUR's own mur binary no longer takes this path — an agent-triggered fleet run execs the mur that shipped with the running runtime, and grants that exact path — but any binary you allowlist by name still can.

"path not entitled" on a path you did grant

The mirror image of the one above: here the kernel allows the access and the tool refuses it. The symptom is a tool error, not an errno —

path not entitled: /Users/you/.mur/artifacts/shot.png
  (grant it via `mur agent perm allow-read`)

— on a path mur agent perm show lists as granted.

This happened when the grant was written with ~. The sandbox builder expanded it; the in-process tool gate compared the literal string, which never matches. The same gap made a ~/.ssh deny entry inert at the tool layer — the form mur agent doctor suggests — and on Linux that gate is the only place deny-within-allow is enforced at all, because Landlock cannot express it.

Fixed in v2.71.0. If you are on an older build, write grants as absolute paths:

mur agent perm allow-read <agent> /Users/you/.mur/artifacts   # not ~/.mur/...
mur agent restart <agent>

MUR can't create a skill or workflow

The concierge needs write access to MUR's own authoring dirs under ~/.mur. Fresh installs are seeded with it. An agent seeded by an earlier build is not — an upgrade never widens an existing sandbox. Grant them explicitly, and create any that do not exist yet before restarting:

mur agent perm allow-write mur ~/.mur/skills
mur agent perm allow-write mur ~/.mur/workflows
mur agent perm allow-write mur ~/.mur/artifacts
mur agent restart mur

MUR can't create or edit a fleet

That one is deliberate — create fleets yourself with mur fleet create. ~/.mur/fleets/<name>/ holds what governs a fleet: fleet.yaml (members, limits:, HITL pre-approvals) and the .stopped kill-switch. An agent that can write there can widen what a run it triggers may do, or clear the stop on it. So the concierge is not seeded with that grant, and fleet_run gives a run write access only to its own ~/.mur/fleet-state/<name>/, where everything a run writes lives.

An agent seeded by an earlier build may still carry a write grant on ~/.mur/fleets — an upgrade never narrows an existing sandbox either. Check how it is stored, then remove that exact string (quote it if it starts with ~, so your shell doesn't expand it):

mur agent perm show mur
mur agent perm remove-path mur write "~/.mur/fleets"
mur agent restart mur

Some paths can never be granted, whatever you ask for. mur agent perm allow-read / allow-write refuse them, and the sandbox denies them in the kernel even if an older profile already carries the grant:

pathrefused for
another agent's directorywrite — its profile.yaml is that agent's entitlements
another agent's identity.keyread too — reading it is enough to forge that agent's signed events
~/.mur/secretsread and write — provider API keys and the commander token
~/.mur/auth.jsonread and write — your account's access and refresh tokens
~/.mur/identity.keyread and write — the host signing key
the runtime binary, per-agent symlinks, autostart dirswrite — they run before any sandbox applies

An agent's self-protection covers only its own profile.yaml and identity.key.

~/.mur itself is not on the list. Granting it is accepted, and it is a bad idea: it covers the central stores wholesale. Grant the specific subdirectory an agent actually needs — ~/.mur/artifacts, ~/.mur/skills — and if a broad grant is already in a profile, narrow it rather than relying on the refusals above:

mur agent perm show <name> filesystem       # see what is granted
mur agent perm deny-path <name> ~/.mur/secrets   # deny wins over a grant
mur agent restart <name>                    # entitlements are read at startup

mur agent doctor <name> reports grants whose path does not exist and will be dropped when the sandbox seals.

A provider keeps answering 429

When a rate-limited response carries retry-after, MUR waits exactly that long (seconds or an HTTP date, clamped to 120 s) before retrying — up to three times in the agent loop, four in the companion outbox. Without the header it falls back to exponential backoff. The log line and the network-audit entry say which one was used: source=retry-after or source=backoff. Repeated 429s with source=retry-after mean the provider itself is asking you to slow down — lower concurrency or move the agent to another model.

Patterns not being injected

  1. Check that sync is working: mur sync
  2. Verify your patterns exist: mur stats
  3. Check what would be injected: mur context

Semantic search not working

  1. Ensure you have a search provider configured in ~/.mur/config.yaml
  2. Rebuild the index: mur reindex
  3. For Ollama: make sure the model is pulled (ollama pull qwen3-embedding)
  4. For OpenAI: verify your API key is set

Pattern extraction fails

  1. Check your LLM provider config under learning.llm in config
  2. Verify API keys are set in your environment
  3. Ensure session recordings exist: mur session list

Migrating from v1

If you previously used the Go version (v1):

# Install v2
brew tap mur-run/tap && brew install mur

v2 reads v1 patterns automatically — no migration step required.

Index corrupted

The vector index is fully rebuildable from YAML:

mur reindex

Hooks not firing

If Claude Code hooks aren't triggering:

# Reinstall hooks
mur init --hooks

This updates ~/.claude/settings.json with the necessary hook configuration.

Every commit fails with "Not a git repository" after renaming a repo

mur project index installs a post-commit hook that re-indexes the repo on every commit. Older hooks baked the repo's absolute path into that block, so renaming the directory left every commit in it failing:

Not a git repository: /Users/you/Projects/old-name

From MUR 2.71.23 the hook carries no path. It runs mur project index --main-repo and MUR resolves the repository from the current directory, following a linked worktree back to its primary repo — so a commit made from a worktree still refreshes the main repo's index, and renaming can no longer strand the hook.

Installed hooks upgrade themselves. The block carries # hook-version: 2, and an older block is rewritten in place on the next index run — which the hook itself triggers — leaving anything you keep before or after it untouched.

A repo you have already renamed is the exception: its old hook can no longer run, so nothing triggers the upgrade. Index it once by hand:

cd path/to/renamed-repo
mur project index

Getting Help