medusajs/medusa · warning

Primary keys are not defined by the module ${keyName}. Setti

Error message

Primary keys are not defined by the module ${keyName}. Setting default primary key to 'id'

What it means

When Medusa registers a loaded module in medusa-module.ts, it reads the module's joinerConfig() to learn its primary keys for the Query/remote joiner. If primaryKeys is missing or empty, it warns and falls back to ['id']. Queries will work only if the underlying data model actually has an `id` column as its key.

Source

Thrown at packages/core/modules-sdk/src/medusa-module.ts:684

        try {
          // TODO: rework that to store on a separate property
          joinerConfig =
            typeof services[keyName].__joinerConfig === "function"
              ? await services[keyName].__joinerConfig?.()
              : services[keyName].__joinerConfig
        } catch {
          // noop
        }

        if (!joinerConfig) {
          throw new Error(
            `Your module is missing a joiner config: ${keyName}. If this module is not queryable, please set { definition: { isQueryable: false } } in your module configuration.`
          )
        }

        if (!joinerConfig.primaryKeys) {
          logger_.warn(
            `Primary keys are not defined by the module ${keyName}. Setting default primary key to 'id'${EOL}`
          )

          joinerConfig.primaryKeys = ["id"]
        }

        joinerConfig = {
          ...joinerConfig,
          databaseClientUrl: resolveJoinerConfigDatabaseClientUrl(resolution),
        }

        services[keyName].__joinerConfig = joinerConfig
        MedusaModule.setJoinerConfig(keyName, joinerConfig)
      }

      MedusaModule.setModuleResolution(keyName, resolution)

      MedusaModule.registerModule(keyName, {

View on GitHub (pinned to 5e06e544a2)

Solutions

  1. Add primaryKeys to the module's joiner config: return { ...defineJoinerConfig(...), primaryKeys: ["id"] } matching the data model's actual key column
  2. If the key column is not `id`, list it explicitly (e.g. primaryKeys: ["code"]) so remote-query filters hit the right column
  3. If the module should not be queryable at all, set definition: { isQueryable: false } in its module declaration

Example fix

// before
export const joinerConfig = defineJoinerConfig(MODULE_MODEL)
// no primaryKeys -> defaults to id

// after
export const joinerConfig = defineJoinerConfig(MODULE_MODEL, {
  primaryKeys: ["id"],
})
Defensive patterns

Strategy: validation

Validate before calling

const joiner = module.joinerConfig()
if (!joiner.primaryKeys?.length) {
  throw new Error(`${keyName} joiner config missing primaryKeys`)
}

Type guard

const hasPrimaryKeys = (c: any): c is { primaryKeys: string[] } =>
  Array.isArray(c?.primaryKeys) && c.primaryKeys.length > 0

Prevention

When it happens

Trigger: A custom module whose defineJoinerConfig (or joinerConfig return) omits `primaryKeys`, or returns it as an empty array, while its service is registered as queryable.

Common situations: Writing a first custom module and copying a joiner config template without filling in primaryKeys; modules keyed on a differently named column (e.g. code) — queries then filter on a non-existent id and return nothing or throw SQL errors.

Understand the failure class

Background: "X is required", "must be set", "cannot be empty": the missing-required-config error family, from Vertex AI project/location to WeChat keys — this error's family across 18 libraries.

Related errors


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