multica-ai/multica · error
mirror %s: %w
Error message
mirror %s: %w
What it means
While mirroring one entry from the shared Hermes home into the task overlay, linkSharedHermesEntry failed for that named entry. This wraps any of the per-entry failures: removing a stale destination ('remove stale'), stat-ing the source ('stat'), or creating the symlink itself (createDirLink/createFileLink, plain os.Symlink on POSIX, copy fallback on Windows). The %s names the entry (e.g. .env, auth.json).
Source
Thrown at server/internal/daemon/execenv/hermes_home.go:538
entries, err := os.ReadDir(sharedHome)
if err != nil {
if os.IsNotExist(err) {
// No shared home to mirror. The derived config + bound skills still
// give Hermes a working home, so this is not fatal on its own.
return reconcileMirroredEntries(hermesHome, nil)
}
return fmt.Errorf("read shared home: %w", err)
}
mirrored := make(map[string]struct{}, len(entries))
for _, entry := range entries {
name := entry.Name()
if isHermesOverlayOwnedEntry(name) {
continue
}
src := filepath.Join(sharedHome, name)
dst := filepath.Join(hermesHome, name)
if err := linkSharedHermesEntry(src, dst); err != nil {
return fmt.Errorf("mirror %s: %w", name, err)
}
mirrored[name] = struct{}{}
}
return reconcileMirroredEntries(hermesHome, mirrored)
}
// reconcileMirroredEntries removes overlay entries that are neither overlay-owned
// nor currently mirrored from the shared home, so a shared entry deleted between
// runs (or a Windows copy-fallback left behind) doesn't survive as stale state.
func reconcileMirroredEntries(hermesHome string, mirrored map[string]struct{}) error {
entries, err := os.ReadDir(hermesHome)
if err != nil {
return fmt.Errorf("read overlay home: %w", err)
}
for _, entry := range entries {
name := entry.Name()
if isHermesOverlayOwnedEntry(name) {
continueView on GitHub (pinned to 2c0912b6ec)
Solutions
- Inspect the named entry in both the shared home and the overlay dir: `ls -la` both paths, look for root-owned or read-only leftovers.
- Delete the stale overlay entry (or the whole env overlay dir under RootDir/hermes-home) so the next prepare recreates it cleanly.
- On Windows, enable Developer Mode or run the daemon with symlink privilege so os.Symlink works.
- Ensure only one daemon instance prepares a given env at a time; two writers racing on one overlay is not supported.
Defensive patterns
Strategy: try-catch
Try / catch
if err := mirrorSharedHermesHome(sharedHome, hermesHome, logger); err != nil {
if strings.HasPrefix(err.Error(), "mirror ") {
// one entry is bad: wipe the regenerable overlay and retry once
if rmErr := os.RemoveAll(hermesHome); rmErr == nil {
return mirrorSharedHermesHome(sharedHome, hermesHome, logger)
}
}
return err
} Prevention
- Ensure Windows symlink privilege (Developer Mode) when overlays live on Windows.
- Never share one env RootDir between concurrently running daemons.
- Keep overlay dirs owned by the daemon's own UID.
When it happens
Trigger: A specific entry in the shared home is unreadable or its existing overlay counterpart is undeletable: dst left root-owned from a previous privileged run; Windows symlink creation without Developer Mode/privilege; EEXIST race with a concurrent task reusing the same overlay; source entry is a symlink loop.
Common situations: Mixed-privilege daemons (some tasks ran as root, later ones as a normal user); Windows hosts without symlink privilege; two daemon replicas preparing the same env RootDir concurrently; an antivirus/backup tool briefly holding the entry.
Related errors
- remove stale %s: %w
- reconcile stale %s: %w
- remove legacy task state %s: %w
- stat %s: %w
- clear hermes skills dir: %w
AI-assisted analysis of multica-ai/multica@2c0912b6ec (2026-08-15).
Data as JSON: /api/errors/b8f78f4599326a9c.
Report an issue: GitHub.