multica-ai/multica · error

remove stale %s: %w

Error message

remove stale %s: %w

What it means

linkSharedHermesEntry found an existing entry at the destination whose symlink does not already point at src, tried os.RemoveAll to clear it before recreating the link, and the removal failed. The function is idempotent by design (matching link left alone, anything else replaced), so this means the overlay holds an entry the daemon cannot delete.

Source

Thrown at server/internal/daemon/execenv/hermes_home.go:615

	}
	return writeFileAtomic(marker, []byte("task-local Hermes state\n"), 0o600)
}

// linkSharedHermesEntry symlinks dst → src, idempotent across Reuse: an existing
// link already pointing at src is left alone; anything else is removed and
// recreated so the overlay never drifts from the shared home. A dangling source
// (a broken symlink in the user's home) is skipped, not failed. Directories use
// createDirLink and files createFileLink so the Windows copy fallbacks match the
// entry kind.
func linkSharedHermesEntry(src, dst string) error {
	if fi, err := os.Lstat(dst); err == nil {
		if fi.Mode()&os.ModeSymlink != 0 {
			if target, err := os.Readlink(dst); err == nil && target == src {
				return nil
			}
		}
		if err := os.RemoveAll(dst); err != nil {
			return fmt.Errorf("remove stale %s: %w", dst, err)
		}
	}

	info, err := os.Stat(src) // follow the link to decide dir vs file
	if err != nil {
		if os.IsNotExist(err) {
			return nil // dangling source in the user's home — nothing to link
		}
		return fmt.Errorf("stat %s: %w", src, err)
	}
	if info.IsDir() {
		return createDirLink(src, dst)
	}
	return createFileLink(src, dst)
}

// writeDerivedHermesConfig writes the task-local config.yaml: the user's config
// with `skills.external_dirs` set to their existing external dirs plus the shared

View on GitHub (pinned to 2c0912b6ec)

Solutions

  1. Check ownership and mode of the named dst path: `ls -la`; chown/chmod it (or its parent) so the daemon can unlink.
  2. Terminate any process still using the overlay entry, then retry the task.
  3. Reset the overlay wholesale (delete hermes-home under the env RootDir) — every mirrored entry is regenerated from the shared home.
  4. Run the daemon under one stable UID so this class of drift cannot occur.
Defensive patterns

Strategy: try-catch

Try / catch

if err := linkSharedHermesEntry(src, dst); err != nil {
	var pe *os.PathError
	if errors.As(err, &pe) && errors.Is(pe.Err, syscall.EACCES) {
		// undeletable stale dst: drop whole overlay and let caller rebuild
		return os.RemoveAll(filepath.Dir(dst))
	}
	return err
}

Prevention

When it happens

Trigger: Destination entry is owned by another user or read-only; a running process holds it open (Windows sharing violation); immutable flag or sticky-bit dir prevents unlink; the entry is a directory the daemon lacks write permission on for recursive removal.

Common situations: Overlay reused across runs under different UIDs/privileges; previous task's process still running out of the overlay; antivirus or indexer briefly pinning files on Windows.

Related errors


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