multica-ai/multica · error

create hermes memory staging dir in %s: %w

Error message

create hermes memory staging dir in %s: %w

What it means

Returned by newHermesStoreStaging when os.MkdirTemp fails to create the dot-prefixed scratch dir (.<store>.migrating-*) inside the store's parent. This staging dir is what the migration copies into before one atomic rename publishes it, so failure stops migration before anything is touched.

Source

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

	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.
// Shared by the memory migration here and the session-database migration in
// hermes_sessions.go: both delete their source once the copy reports success,
// so both need the same all-or-nothing publish.
// MkdirTemp already creates it 0700, the same mode the store itself is created
// with, so there is no follow-up chmod that could fail and strand it.
func newHermesStoreStaging(storeDir string) (string, error) {
	parent := filepath.Dir(storeDir)
	if err := os.MkdirAll(parent, 0o700); err != nil {
		return "", fmt.Errorf("create hermes memory store parent %s: %w", parent, err)
	}
	staging, err := os.MkdirTemp(parent, "."+filepath.Base(storeDir)+".migrating-")
	if err != nil {
		return "", fmt.Errorf("create hermes memory staging dir in %s: %w", parent, err)
	}
	return staging, nil
}

// promoteHermesStoreStaging publishes a fully-copied staging dir as the store,
// reporting whether this caller won. The empty store dir (if any) is removed
// first: os.Remove only succeeds on an empty directory, so a store another task
// already populated makes this a no-op, and Windows rejects a rename onto an
// existing directory outright.
//
// Losing the race is not an error — the winner's store holds the same agent's
// state — but it has to be positively confirmed, never inferred from any
// failure. The caller deletes the source directory whenever this returns
// (false, nil), so a permission error, a read-only filesystem or a Windows
// sharing violation must fail closed instead of passing for "someone else
// published".
//
// published answers "did a competitor already publish real state here?" and is

View on GitHub (pinned to 2c0912b6ec)

Solutions

  1. Freeze space/inodes on the volume containing the profile dir and retry the task.
  2. Fix write permissions on <profile>/hermes-state/<agent> for the daemon user.
  3. If it recurs under many concurrent tasks, stagger task starts to reduce simultaneous migrations.
Defensive patterns

Strategy: retry

Validate before calling

// Cheap canary: if a temp dir can be made, MkdirTemp will likely succeed.
if _, err := os.MkdirTemp(parent, ".canary-"); err != nil {
    return fmt.Errorf("staging area unusable: %w", err)
} else { os.Remove(canary) }

Try / catch

if _, err := newHermesStoreStaging(storeDir); err != nil && isTransient(err) {
    // EMFILE/ENOSPC class: back off once, then retry
    time.Sleep(time.Second)
    _, err = newHermesStoreStaging(storeDir)
}

Prevention

When it happens

Trigger: os.MkdirTemp in the store parent fails: parent not writable (EACCES), disk full (ENOSPC), too many open files (EMFILE) under heavy concurrency, or file-name pattern issues on exotic filesystems.

Common situations: Disk-full on the profile volume during an upgrade with large memories; read-only mount; heavy concurrent task starts exhausting temp-dir handles.

Related errors


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