multica-ai/multica · error

migrate task-local hermes memory %s into %s: %w

Error message

migrate task-local hermes memory %s into %s: %w

What it means

Returned by migrateHermesTaskMemories when copyHermesMemoryEntry fails while copying one entry (file or subdir) from the task-local memories dir into the staging dir. Because migration must be all-or-nothing (source is deleted only after a full copy), any single-entry failure aborts the whole migration and the mount.

Source

Thrown at server/internal/daemon/execenv/hermes_memory.go:237

	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)
		}
	}

	promoted, err := promoteHermesStoreStaging(staging, storeDir, hermesStorePopulated)
	if err != nil {
		return err
	}
	if !promoted {
		logger.Info("execenv: another task populated the hermes memory store first; keeping it", "store", storeDir)
		return nil
	}
	logger.Info("execenv: migrated task-local hermes memories into agent store", "store", storeDir, "entries", len(entries))
	return nil
}

// newHermesStoreStaging creates the scratch directory a store migration copies
// into. It is a sibling of the store so the promoting rename stays within one
// filesystem, and dot-prefixed so it is obvious it is not a store of its own.

View on GitHub (pinned to 2c0912b6ec)

Solutions

  1. Freeze/clean the volume holding the profile dir (staging is a sibling of the store) and retry the task.
  2. Inspect the named src entry from the error message: fix its permissions or remove it if it is junk (broken symlink, socket) that Hermes never needed.
  3. Ensure the daemon user can read every file under the task-local memories dir.
Defensive patterns

Strategy: retry

Validate before calling

// Free-space sanity check before a migration copies bytes:
var st syscall.Statfs_t
if err := syscall.Statfs(filepath.Dir(storeDir), &st); err == nil && st.Bavail*uint64(st.Bsize) < needBytes {
    return errors.New("insufficient space for memory migration")
}

Try / catch

if err := migrateHermesTaskMemories(src, store, logger); err != nil {
    if isTransientCopyErr(err) { // ENOSPC resolved, EACCES fixed
        err = migrateHermesTaskMemories(src, store, logger) // source untouched on failure, retry is safe
    }
}

Prevention

When it happens

Trigger: copyHermesMemoryEntry fails: source file unreadable (EACCES), disk full while copying into staging, an entry whose type changed between ReadDir and copy (symlink/dir race), or an irregular file type the copier rejects.

Common situations: Upgrading a daemon on a nearly-full disk; a memories tree containing odd entries (sockets, broken symlinks) written by a crashed task; permission mismatch between overlay owner and daemon user.

Related errors


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