multica-ai/multica · error

restrict task-local CLI config directory: %w

Error message

restrict task-local CLI config directory: %w

What it means

In task-local mode (MULTICA_TASK_CONFIG_ROOT set) the save path walks from the config directory up to the task root, chmod'ing each level to 0700 so the daemon-private config is not world-readable. This error means one of those os.Chmod calls failed, typically with EPERM because the directory is owned by another user.

Source

Thrown at server/internal/cli/config.go:337

	if err != nil {
		return err
	}
	dir := filepath.Dir(path)
	dirMode := os.FileMode(0o755)
	if strings.TrimSpace(os.Getenv(TaskConfigRootEnv)) != "" {
		dirMode = 0o700
	}
	if err := os.MkdirAll(dir, dirMode); err != nil {
		return fmt.Errorf("create CLI config directory: %w", err)
	}
	if dirMode == 0o700 {
		root, _, err := multicaConfigRoot()
		if err != nil {
			return fmt.Errorf("resolve task-local CLI config root: %w", err)
		}
		for current := dir; ; current = filepath.Dir(current) {
			if err := os.Chmod(current, 0o700); err != nil {
				return fmt.Errorf("restrict task-local CLI config directory: %w", err)
			}
			if current == root {
				break
			}
			parent := filepath.Dir(current)
			if parent == current {
				return fmt.Errorf("task-local CLI config directory %q escapes root %q", dir, root)
			}
		}
	}
	data, err := json.MarshalIndent(cfg, "", "  ")
	if err != nil {
		return fmt.Errorf("encode CLI config: %w", err)
	}

	// Write to a temp file in the same directory, then rename for atomicity.
	tmp, err := os.CreateTemp(dir, ".config-*.json.tmp")
	if err != nil {

View on GitHub (pinned to 2c0912b6ec)

Solutions

  1. Align ownership: chown the entire task root to the user the CLI runs as
  2. Run the save as the same user that owns the directory chain
  3. Point MULTICA_TASK_CONFIG_ROOT at a plain local directory (ext4/apfs) rather than a network/FUSE mount
  4. If 0700 restriction is not needed for your setup, unset MULTICA_TASK_CONFIG_ROOT to use the standard 0755 home layout

Example fix

# before: daemon (root) created /var/lib/multica/tasks; CLI runs as 'multica'
$ multica config set ...
restrict task-local CLI config directory: chmod /var/lib/multica/tasks: operation not permitted

# after
$ sudo chown -R multica:multica /var/lib/multica/tasks
Defensive patterns

Strategy: validation

Validate before calling

root := os.Getenv("MULTICA_TASK_CONFIG_ROOT")
for p := root; ; p = filepath.Dir(p) {
	if info, err := os.Stat(p); err != nil || info.Mode().Perm()&0o200 == 0 {
		// ancestor not writable by this user; fix before saving
	}
	if p == "/" { break }
}

Try / catch

if err := cli.SaveCLIConfig(cfg); err != nil && strings.Contains(err.Error(), "restrict task-local") {
	// report ownership mismatch between CLI user and task-root owner
}

Prevention

When it happens

Trigger: Saving CLI config in task-local mode when a directory between the config dir and the root is owned by root or another user, or when running on a filesystem that does not support chmod (some NFS/FUSE/Windows mounts return EPERM).

Common situations: Daemon created the task root as root and then a CLI subprocess running as the service user tries to save config; MULTICA_TASK_CONFIG_ROOT pointing at a bind-mounted or network volume with restricted metadata operations.

Related errors


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