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
- Add primaryKeys to the module's joiner config: return { ...defineJoinerConfig(...), primaryKeys: ["id"] } matching the data model's actual key column
- If the key column is not `id`, list it explicitly (e.g. primaryKeys: ["code"]) so remote-query filters hit the right column
- 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
- Always set primaryKeys in defineJoinerConfig for custom modules
- Set isQueryable:false for non-queryable modules
- Add a unit test asserting joinerConfig().primaryKeys matches the model keys
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
- Invalid operator: ${operator}
- Duplicate cross-module join target table "${join.target.tabl
- Cross-module join for "${join.target.table}" cannot be its o
- Cross-module join for "${join.target.table}" references unkn
- Unable to load resources for module ${modulePath} automagica
AI-assisted analysis of medusajs/medusa@5e06e544a2 (2026-08-27).
Data as JSON: /api/errors/b697aba6f12131f0.
Report an issue: GitHub.