medusajs/medusa · error · CloudServiceError

CloudServiceError propagated from non-ok response (body.mess

Error message

CloudServiceError propagated from non-ok response (body.message, status ${response.status})

What it means

The Medusa search HTTP client wraps any non-2xx response from the cloud search service in a CloudServiceError, propagating the remote body's type/originalType/data/message plus the HTTP status. This is the transport-level surfacing of upstream API failures (auth, not-found, bad request, service errors).

Source

Thrown at packages/modules/search/src/providers/search-medusa/utils/client.ts:114

    const headers: Record<string, string> = {
      "Content-Type": "application/json",
      Authorization: `Basic ${this.options_.api_key}`,
      "x-medusa-environment-handle": this.options_.environment_handle,
    }

    const response = await fetch(`${this.options_.endpoint}${path}`, {
      method,
      headers: {
        ...options.headers,
        ...headers,
      },
      body: options.body ? JSON.stringify(options.body) : undefined,
    })

    const body = await response.json().catch(() => ({}))

    if (!response.ok) {
      throw new CloudServiceError(
        body.type,
        body.originalType,
        body.data,
        body.message,
        response.status
      )
    }

    return body as T
  }
}

export class MedusaSearchIndex {
  constructor(
    protected readonly client_: MedusaSearchClient,
    protected readonly name_: string
  ) {}

View on GitHub (pinned to 5e06e544a2)

Solutions

  1. Inspect the CloudServiceError status and message to identify the cause (401/403 -> credentials, 404 -> index/handle, 400 -> payload)
  2. Verify api_key, endpoint, and environment_handle provider options against the cloud dashboard
  3. If the index is missing, create it (createIndex / loadIndex) before writing
  4. Retry with backoff for transient 5xx statuses
Defensive patterns

Strategy: retry

Type guard

const isCloudServiceError = (e) =>
  e instanceof Error && "status" in e && typeof e.status === "number"

Try / catch

for (let attempt = 0; attempt < 3; attempt++) {
  try { return await client.write(...) } catch (e) {
    if (isCloudServiceError(e) && e.status >= 500 && attempt < 2) {
      await new Promise((r) => setTimeout(r, 2 ** attempt * 500)); continue
    }
    throw e
  }
}

Prevention

When it happens

Trigger: Any client operation — createIndex, page, schema, metadata, updateSchema, or write — where the remote endpoint returns a non-ok status: expired/invalid api_key (401/403), unknown index/environment handle (404), malformed query payload (400), or upstream outage (5xx).

Common situations: Expired or rotated API keys, wrong endpoint or environment_handle in provider options, an index not yet created when writing documents, or transient 5xx outages of the Medusa cloud search service during reindex jobs.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


AI-assisted analysis of medusajs/medusa@5e06e544a2 (2026-08-27). Data as JSON: /api/errors/4d3431f04b2a6081. Report an issue: GitHub.