multica-ai/multica · error
read task-local hermes memories %s: %w
Error message
read task-local hermes memories %s: %w
What it means
Returned by migrateHermesTaskMemories when os.ReadDir on the task-local memories source dir fails. The migration cannot know which entries to copy, and skipping would mean silently dropping the agent's remembered context, so the overlay mount aborts.
Source
Thrown at server/internal/daemon/execenv/hermes_memory.go:222
// half-populated store. First writer wins; the loser discards its staging
// and takes the winner's store, which is the same outcome as finding a
// store that was already populated.
//
// Only an absent or empty store is migrated into, so an upgrade never
// overwrites memory the agent has already accumulated.
func migrateHermesTaskMemories(taskDir, storeDir string, logger *slog.Logger) error {
stored, err := os.ReadDir(storeDir)
switch {
case err == nil && len(stored) > 0:
return nil // store already holds this agent's memory — never overwrite it
case err != nil && !os.IsNotExist(err):
// Treating this as "nothing to migrate" would delete the source below.
return fmt.Errorf("read hermes memory store %s: %w", storeDir, err)
}
entries, err := os.ReadDir(taskDir)
if err != nil {
return fmt.Errorf("read task-local hermes memories %s: %w", taskDir, err)
}
if len(entries) == 0 {
return nil
}
staging, err := newHermesStoreStaging(storeDir)
if err != nil {
return err
}
defer os.RemoveAll(staging) // no-op once the staging dir has been promoted
for _, entry := range entries {
src := filepath.Join(taskDir, entry.Name())
if err := copyHermesMemoryEntry(src, filepath.Join(staging, entry.Name()), entry); err != nil {
return fmt.Errorf("migrate task-local hermes memory %s into %s: %w", src, storeDir, err)
}
}
View on GitHub (pinned to 2c0912b6ec)
Solutions
- Fix permissions on the task-local memories dir or re-run the task under the user that owns the overlay.
- If the task overlay is disposable, delete the whole task home so it is rebuilt without a legacy memories dir.
- Inspect the wrapped errno for hardware/mount errors before retrying.
Defensive patterns
Strategy: validation
Validate before calling
if ents, err := os.ReadDir(taskDir); err != nil {
return fmt.Errorf("task memories unreadable, fix or clear %s: %w", taskDir, err)
} else if len(ents) == 0 {
// migration is a no-op; safe to proceed
} Prevention
- Run tasks and daemon under the same user
- Treat task overlay dirs as owned by the daemon
- Check FS health when EIO appears in the wrapped error
When it happens
Trigger: os.ReadDir(<task-hermes-home>/memories) fails with EACCES (dir not readable by daemon user), EIO, or the path exists as something unreadable even though Lstat called it a directory earlier.
Common situations: Task overlay dirs written by a root-run daemon then read by a user-run daemon; corrupted overlay after a crash; FS-level errors on the volume backing task homes.
Related errors
- remove stale memories path %s: %w
- read hermes memory store %s: %w
- create hermes memory store parent %s: %w
- mint PAT: response missing token
- daemon profile is not resolved yet; token sync skipped
AI-assisted analysis of multica-ai/multica@2c0912b6ec (2026-08-15).
Data as JSON: /api/errors/dbb1d3e93e3834b7.
Report an issue: GitHub.