[Bug]: column "product_id" does not exist when querying product.options (*options) — 2.16+ ProductProductOption pivot mapping
Bug report
Querying products with the options relation expanded (e.g. fields=id,*options on /store/products or /admin/products, or the admin dashboard's product option UI) throws:
{"type":"invalid_data","message":"column p0.product_id does not exist"}The exact alias in the error varies by call site (seen both p0.product_id and o3.product_id from the admin dashboard), but the failure is consistent: any query expanding Product.options fails.
Root cause (as far as I can tell)
Since 2.16.0, @medusajs/product defines two parallel relations between Product and ProductOption:
// product.js
options: model.manyToMany(() => ProductOption, {
pivotEntity: () => ProductProductOption,
}),
product_options: model.hasMany(() => ProductProductOption, {
mappedBy: "product",
}),// product-product-option.js (pivot entity, @since 2.16.0)
const ProductProductOption = model.define("ProductProductOption", {
id: model.id({ prefix: "prodopt" }).primaryKey(),
product: model.belongsTo(() => Product, { mappedBy: "options" }),
product_option: model.belongsTo(() => ProductOption, { mappedBy: "products" }),
values: model.manyToMany(() => ProductOptionValue, {
pivotEntity: () => ProductProductOptionValue,
}),
});The product_product_option table itself has a correct product_id column (confirmed via \d product_product_option and a manual join, both work fine). But MikroORM's auto-generated join for the older options many-to-many relation (via pivotEntity) appears to build the query against the wrong table alias, producing a product_id reference on an alias that doesn't have that column — likely a collision between the options (manyToMany + pivotEntity) and product_options (hasMany, mappedBy: "product") relations pointing at the same pivot table.
Reproduction
curl 'http://localhost:9000/store/products?limit=1&fields=id,*options' \
-H 'x-publishable-api-key: <key>'
# => {"type":"invalid_data","message":"column p0.product_id does not exist"}
# Works fine without *options:
curl 'http://localhost:9000/store/products?limit=1&fields=id,title'
curl 'http://localhost:9000/store/products?limit=1&fields=id,*variants'
curl 'http://localhost:9000/store/products?limit=1&fields=id,*variants.prices'
# Manual SQL confirms the data/schema is correct:
SELECT po.id, po.title, ppo.product_id
FROM product_option po
LEFT JOIN product_product_option ppo ON ppo.product_option_id = po.id
LIMIT 10;
-- returns correct rowsAlso reproducible in the Admin dashboard UI (product option editing surfaces the same error client-side, e.g. column o3.product_id does not exist).
Environment
@medusajs/medusa: 2.19.0 (also present in 2.14.2, so this predates at least a few minor versions — possibly present since the 2.16.0product_optionsrefactor)- Node: v22.22.1
- PostgreSQL: 18.4
- Package manager: pnpm 10.11.1
Expected behavior
Expanding *options on a product query should return the product's options via the product_product_option pivot table without error, same as expanding *variants or *categories works today.
Additional notes
db:migratereports theproductmodule as fully up to date; this is not a missing-migration issue.- The
optionsfield is not currently required by our storefront, so this is not blocking for us, but it does break admin UI functionality that relies on the option relation.
Source: medusajs/medusa