multica-ai/multica · error

daemon-managed task requires a task-local Multica config…

Error message

daemon-managed task requires a task-local Multica config root in %s%s

What it means

requireTaskLocalConfigRoot guards config-reading commands (config show, auth status, etc.) in daemon-managed execution: they must load from the task-local config root injected via cli.TaskConfigRootEnv, never the human's global config. This variant fires when a leftover workdir marker is the only daemon signal, and names the exact marker file so the user can recover.

Solutions

  1. Delete the marker file named in the appended leftoverMarkerSuffix.
  2. Re-run the config command from outside the affected directory tree to confirm recovery.
  3. If you actually are inside a real daemon task, ensure the daemon exports the task-local config root in the env var named in the message before running config commands.

Example fix

# before
multica config show
# -> daemon-managed task requires a task-local Multica config root in ...

# after
rm /path/to/repo/.multica/daemon-task-marker.json
multica config show
Defensive patterns

Strategy: validation

Validate before calling

# Before `multica config show` in any repo, confirm no stale marker exists:
if find . -name daemon-task-marker.json -path '*/.multica/*' | grep -q .; then
  echo "stale daemon marker present; remove it before running config commands" >&2
  exit 1
fi

Prevention

When it happens

Trigger: inDaemonManagedExecutionContext() is true purely from a stale marker file (crashed local_directory daemon task), the TaskConfigRootEnv variable is empty/unset, and the user runs a config command in that directory tree.

Common situations: Same MUL-6132 leftover-marker situation as newAPIClient's refusal, hit through `multica config show` or `multica auth status` instead of an API command; deliberately parallel wording so which command ran first does not change actionability.

Related errors


AI-assisted analysis of multica-ai/multica@2c0912b6ec (2026-08-15). Data as JSON: /api/errors/ab3ac4a975e6f9d5. Report an issue: GitHub.

Appendix: source

Thrown at server/cmd/multica/cmd_agent.go:415

	return "; MULTICA_DAEMON_PORT is set without task identity — if this is a host or container startup shell, remove that variable and retry"
}

// requireTaskLocalConfigRoot prevents daemon-managed subprocesses that lost
// part of their injected environment from silently resolving Multica state
// below the daemon owner's HOME. Commands that intentionally support task-local
// config (currently config show/set and auth status) call this before any load.
func requireTaskLocalConfigRoot() error {
	if !inDaemonManagedExecutionContext() {
		return nil
	}
	if strings.TrimSpace(os.Getenv(cli.TaskConfigRootEnv)) == "" {
		// The third refusal path a leftover marker can trigger, alongside
		// newAPIClient and requireHumanLocalCommand. It has to name the file
		// too: a user hitting this one through `config show` or `auth status`
		// is as stuck as one hitting the others, and "which command did you
		// happen to run first" must not decide whether the error is actionable.
		if markerPath := leftoverDaemonTaskMarkerPath(); markerPath != "" {
			return fmt.Errorf("daemon-managed task requires a task-local Multica config root in %s%s", cli.TaskConfigRootEnv, leftoverMarkerSuffix(markerPath))
		}
		return fmt.Errorf("daemon-managed task requires a task-local Multica config root in %s%s", cli.TaskConfigRootEnv, daemonPortOnlyContextHint())
	}
	return nil
}

// requireHumanLocalCommand rejects commands whose purpose is to authenticate,
// set up, or operate the human-owned local daemon/profile. Task API commands
// remain available with the injected mat_ token; these local commands do not.
func requireHumanLocalCommand(command string) error {
	if !inDaemonTaskIdentityContext() {
		return nil
	}
	// A task-scoped workdir marker with no task identity in the environment is
	// the one signal that can outlive the task that wrote it: a local_directory
	// run that never cleaned up leaves it in the user's own repository, where it
	// disables every command below this function for that whole directory tree
	// until someone deletes the file by hand (MUL-6132). Name it, so the user

View on GitHub (pinned to 2c0912b6ec)