multica-ai/multica · error

Upload failed: ${res.status}

Error message

Upload failed: ${res.status}

What it means

Wrap thrown by prepareHermesHome when mountHermesSessionDB fails. When a persistent session store is configured, the overlay's state.db is linked to it so the conversation transcript survives the task; the mount result also reports whether history is actually present so the caller never promises a resume that is not there. Contrary to some comments, this link failing IS fatal here — though a store that is merely absent leaves the database task-local by design.

Source

Thrown at packages/core/api/client.ts:2653

    if (opts?.chatSessionId) formData.append("chat_session_id", opts.chatSessionId);

    const rid = createRequestId();
    const start = Date.now();
    this.logger.info("→ POST /api/upload-file", { rid });

    const res = await fetch(`${this.baseUrl}/api/upload-file`, {
      method: "POST",
      headers: this.authHeaders(),
      body: formData,
      credentials: "include",
      signal,
    });

    if (!res.ok) {
      if (res.status === 401) this.handleUnauthorized();
      const message = await this.parseErrorMessage(res, `Upload failed: ${res.status}`);
      this.logger.error(`← ${res.status} /api/upload-file`, { rid, duration: `${Date.now() - start}ms`, error: message });
      throw new Error(message);
    }

    this.logger.info(`← ${res.status} /api/upload-file`, { rid, duration: `${Date.now() - start}ms` });
    const raw = (await res.json()) as unknown;
    return parseWithFallback(raw, AttachmentResponseSchema, EMPTY_ATTACHMENT, {
      endpoint: "POST /api/upload-file",
    });
  }

  // Chat Sessions
  async listChatSessions(
    params?: { status?: string },
    workspaceSlug?: string,
  ): Promise<ChatSession[]> {
    const query = params?.status ? `?status=${params.status}` : "";
    return this.fetch(`/api/chat/sessions${query}`, {
      headers: workspaceHeader(workspaceSlug),
    });

View on GitHub (pinned to 2c0912b6ec)

Solutions

  1. Verify the session store path exists with the expected type and is writable by the daemon user.
  2. Remove the conflicting state.db entry inside the per-task hermes-home so a fresh link can be made.
  3. If the store lives on another filesystem or an unsupported mount, relocate it to the same filesystem as env-root or drop the store config to keep sessions task-local.
  4. Restore/recreate a corrupted conversation store from backup or reset it.
Defensive patterns

Strategy: validation

Validate before calling

if sessionStore != "" {
    fi, err := os.Stat(sessionStore)
    if err != nil {
        return fmt.Errorf("session store unreachable: %w", err)
    }
    if err := checkWritable(filepath.Dir(sessionStore)); err != nil {
        return fmt.Errorf("session store dir not writable: %w", err)
    }
}

Try / catch

if err := prepareHermesHome(...); err != nil {
    if strings.Contains(err.Error(), "mount conversation sessions") {
        // decide: fix the store, or drop sessionStore config to keep sessions task-local (explicit fallback choice)
    }
}

Prevention

When it happens

Trigger: prepareHermesHome is called with a non-empty sessionStore and mountHermesSessionDB(hermesHome, sessionStore, logger) errors — e.g. the store path exists but is not a regular file/dir as expected, linking fails across filesystems or due to permissions, or a conflicting state.db already occupies the target.

Common situations: Session store on a different filesystem than env-root where the link strategy does not apply; store file corrupted or truncated to a non-database; permission drift on the conversation store directory; leftover state.db symlink from an aborted run.

Related errors


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