medusajs/medusa · warning

setLocale is not available in the server environment. Please

Error message

setLocale is not available in the server environment. Please set the locale directly through the 'x-medusa-locale' header.

What it means

The JS SDK's setLocale persists the locale in the browser's localStorage so subsequent requests send x-medusa-locale. It checks for the `window` global; when the SDK runs server-side (SSR, Next.js server components, scripts) there is no window and the call just logs this warning and returns without doing anything — no request is made and no error is thrown.

Source

Thrown at packages/core/js-sdk/src/client.ts:173

      info: console.info,
      debug: console.debug,
    }

    this.logger = {
      ...logger,
      debug: config.debug ? logger.debug : () => {},
    }

    if (hasStorage("localStorage")) {
      this.locale_ = window.localStorage.getItem(LOCALE_STORAGE_KEY) || ""
    }

    this.fetch_ = this.initClient()
  }

  setLocale(locale: string) {
    if (!window) {
      this.logger.warn(
        "setLocale is not available in the server environment. Please set the locale directly through the 'x-medusa-locale' header."
      )
      return
    }

    if (hasStorage("localStorage")) {
      window.localStorage.setItem(LOCALE_STORAGE_KEY, locale)
    }

    this.locale_ = locale
  }

  /**
   * `fetch` closely follows (and uses under the hood) the native `fetch` API. There are, however, few key differences:
   * - Non 2xx statuses throw a `FetchError` with the status code as the `status` property, rather than resolving the promise
   * - You can pass `body` and `query` as objects, and they will be encoded and stringified.
   * - The response gets parsed as JSON if the `accept` header is set to `application/json`, otherwise the raw Response object is returned
   *

View on GitHub (pinned to 5e06e544a2)

Solutions

  1. Pass the locale per request instead: configure the SDK fetch client to send the 'x-medusa-locale' header on the server
  2. Move setLocale calls into client-only code (useEffect, onClick handlers, or components guarded by typeof window !== 'undefined')
  3. Create separate SDK instances for server code with publishable key + custom headers, and keep the localStorage-based one for the browser

Example fix

// before
export const sdk = medusaSdk(...)
sdk.setLocale(locale) // runs in Next.js server component

// after (server)
const sdk = new Client({ baseUrl, publishableKey, customHeaders: { "x-medusa-locale": locale } })
// after (client-only)
useEffect(() => { sdk.setLocale(locale) }, [locale])
Defensive patterns

Strategy: type-guard

Validate before calling

if (typeof window !== "undefined") {
  sdk.setLocale(locale)
} else {
  // pass header per request instead
  client.setFetch((url, init) => fetch(url, { ...init, headers: { ...init?.headers, "x-medusa-locale": locale } }))
}

Type guard

const isBrowser = (): boolean => typeof window !== "undefined"

Prevention

When it happens

Trigger: Calling sdk.setLocale('de') in code that executes in a Node/server runtime: getServerSideProps, Next.js server components, route handlers, unit tests, or SSR rendering of a storefront.

Common situations: Calling setLocale at module top-level of a component that is also server-rendered; shared SDK initialization code imported by both server and client bundles; SSR frameworks hydrating a component that calls setLocale during render.


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