multica-ai/multica · error

link memories to store %s: %w

Error message

link memories to store %s: %w

What it means

Returned by mountHermesMemories when createDirLink fails to link the overlay's memories/ path to the agent-scoped store. This link is the whole point of the mount — without it the agent's memory is task-local again — so failure aborts the overlay.

Source

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

			// Runs before the store dir is created, so migration can publish a
			// fully-copied tree with one atomic rename. Fails closed: the source
			// dir below is only removed once every entry is safely in the store.
			if err := migrateHermesTaskMemories(dst, storeDir, logger); err != nil {
				return err
			}
		}
		if err := os.RemoveAll(dst); err != nil {
			return fmt.Errorf("remove stale memories path %s: %w", dst, err)
		}
	} else if !os.IsNotExist(err) {
		return fmt.Errorf("stat memories path %s: %w", dst, err)
	}

	if err := os.MkdirAll(storeDir, 0o700); err != nil {
		return fmt.Errorf("create hermes memory store %s: %w", storeDir, err)
	}
	if err := createDirLink(storeDir, dst); err != nil {
		return fmt.Errorf("link memories to store %s: %w", storeDir, err)
	}
	// Stamp the store as just-used: mounting it does not touch its mtime, so
	// without this the GC's idle check could reclaim a long-idle agent's memory
	// right as a task picks it back up.
	touchHermesMemoryStore(storeDir, logger)
	return nil
}

// migrateHermesTaskMemories copies the contents of a pre-existing task-local
// memories dir into an empty store, so upgrading a daemon does not drop what an
// in-flight task had already remembered.
//
// The caller removes the source directory as soon as this returns nil, so
// "nil" has to mean "the source is safe to delete" and nothing else. Three
// rules follow from that:
//
//   - It copies rather than moves. A move is faster but fails across
//     filesystems — the workspaces root and the Multica profile dir can be on

View on GitHub (pinned to 2c0912b6ec)

Solutions

  1. Check the wrapped error: ERROR_ALREADY_EXISTS / EEXIST → a race with another task; retry the task once the other finishes.
  2. On Windows, keep the Multica profile dir and task overlays on the same volume as each other.
  3. If symlinks are disabled by policy (Windows Developer Mode off, non-admin), enable them or run the daemon with the privilege to create junctions.
Defensive patterns

Strategy: validation

Validate before calling

// Verify linkability beforehand: target exists and dst is linkable.
if err := os.MkdirAll(storeDir, 0o700); err == nil {
    if _, err := os.Lstat(dst); err == nil {
        // expect the mount to replace it; if this fails at runtime, check perms
    }
}

Try / catch

if err := mountHermesMemories(home, store, logger); err != nil && strings.Contains(err.Error(), "link memories to store") {
    // EEXIST from a racing task: safe to re-invoke once after the race settles
    err = mountHermesMemories(home, store, logger)
}

Prevention

When it happens

Trigger: createDirLink fails to create the store→overlay symlink/junction: the overlay memories path was removed by a racing task between Lstat and link, a symlink already exists at dst, or (Windows) directory junction creation is denied because the target is on another volume or privileges are missing.

Common situations: Two concurrent tasks for the same agent mounting the same overlay home; running on Windows without the rights to create junctions/symlinks when the store is on a different drive; security software reverting symlinks under the overlay.

Related errors


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