multica-ai/multica · error

read shared home: %w

Error message

read shared home: %w

What it means

mirrorSharedHermesHome could not list the shared Hermes home (os.ReadDir) and the failure was not 'does not exist'. The daemon builds each task's Hermes overlay by symlinking entries out of the user's real home, so an unreadable source home aborts overlay construction. The wrapped error is the underlying *PathError (permission denied, not-a-directory, I/O error).

Source

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

	return strings.TrimSpace(s[:eq])
}

// mirrorSharedHermesHome symlinks every top-level entry of the shared ~/.hermes/
// into the per-task home except the overlay-owned entries, then reconciles the
// destination so entries removed from the shared home (or left over from a prior
// reuse) don't linger as readable stale state. Symlinks share state with the
// user's real home (auth/OAuth refreshes propagate, no credential copy lingers
// in task scratch). The shared home itself is never written — we only read it
// and create links pointing into it.
func mirrorSharedHermesHome(sharedHome, hermesHome string, logger *slog.Logger) error {
	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

View on GitHub (pinned to 2c0912b6ec)

Solutions

  1. Check the resolved shared home: run the daemon env with HERMES_HOME printed, verify `ls -la <sharedHome>` works as the daemon user.
  2. Fix ownership/permissions on the shared home (chown/chmod so the daemon's UID can read it).
  3. If HERMES_HOME points at a wrong path (file, mountpoint), point it at the real Hermes home or unset it to use the platform default.
  4. If the volume is flaky (NFS/network share), remount or move the Hermes home to local disk and retry the task.
Defensive patterns

Strategy: validation

Validate before calling

if fi, err := os.Stat(sharedHome); err != nil || !fi.IsDir() {
	return fmt.Errorf("shared hermes home unusable: %v", err)
}
if entries, err := os.ReadDir(sharedHome); err != nil {
	return fmt.Errorf("shared hermes home not listable: %w", err)
} else {
	_ = entries // readable
}

Try / catch

if err := mirrorSharedHermesHome(sharedHome, hermesHome, logger); err != nil {
	var pe *os.PathError
	if errors.As(err, &pe) {
		log.Printf("shared home path failure at %s: %v", pe.Path, pe.Err)
	}
	return err // overlay cannot be trusted if the source home is unreadable
}

Prevention

When it happens

Trigger: Running a task whose resolved HERMES_HOME (or the platform default ~/.hermes, %LOCALAPPDATA%\hermes on Windows) exists but cannot be listed: mode 000, owned by another user, the path is actually a file, or the filesystem errors (NFS stale handle, detached volume).

Common situations: Daemon runs as a different user/service account than the one owning ~/.hermes; HERMES_HOME pointed at a file or a OneDrive/enterprise share with restricted ACLs; home directory on a network mount that went away mid-run.

Related errors


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