chatboxai/chatbox · error · Error

Unmatching product

Error message

Unmatching product

What it means

Thrown by the Lemonsqueezy license validator after a license key validates successfully against the Lemonsqueezy API, but the returned meta.product_id is not in the product_ids list fetched from the app's remote config. It prevents a valid-but-wrong-product key (e.g. a key for a different app/SKU) from granting entitlements in this product.

Source

Thrown at src/renderer/packages/lemonsqueezy.ts:58

      license_key: key,
      instance_name: instanceName,
    }),
  })
  const json: ActivateResponse = await res.json()
  if (!json.activated) {
    if (json.error.includes('This license key has reached the activation limit') && json.license_key) {
      return { valid: false, instanceId: '', error: 'reached_activation_limit' }
    } else if (json.error.includes('This license key is expired.')) {
      return { valid: false, instanceId: '', error: 'expired' }
    } else if (json.error.includes('license_key not found')) {
      return { valid: false, instanceId: '', error: 'not_found' }
    } else {
      throw new Error(json.error)
    }
  }
  const remoteConfig = await remote.getRemoteConfig('product_ids')
  if (!remoteConfig.product_ids.includes(json.meta.product_id)) {
    throw new Error('Unmatching product')
  }
  return { valid: true, instanceId: json.instance.id }
}

export async function deactivateLicense(key: string, instanceId: string) {
  await ofetch('https://api.lemonsqueezy.com/v1/licenses/deactivate', {
    method: 'POST',
    retry: 5,
    body: {
      license_key: key,
      instance_id: instanceId,
    },
  })
}

type ValidateLicenseKeyResponse = {
  valid: boolean
}

View on GitHub (pinned to 81571269ad)

Solutions

  1. Verify the license key is the one issued for THIS product (check the purchase email/product name).
  2. Ensure the device is online so remote config product_ids is current — a stale/empty list can exclude a valid product_id.
  3. If remote config is correct and the key is genuinely for this product, contact support to reconcile the product_id mapping.
Defensive patterns

Strategy: try-catch

Validate before calling

const allowedProductIds = (await remote.getRemoteConfig('product_ids')).product_ids
// cannot fully pre-validate without the key's product_id, but ensure remote config loaded:
if (!allowedProductIds?.length) console.warn('product_ids remote config missing; mismatch error likely')

Type guard

function isLemonsqueezyActivationResponse(json: unknown): json is { meta: { product_id: number }; instance: { id: string } } {
  return !!json && typeof json === 'object' &&
    typeof (json as any).meta?.product_id === 'number' &&
    typeof (json as any).instance?.id === 'string'
}

Try / catch

try {
  await activateLicense(key)
} catch (e) {
  if (e instanceof Error && e.message === 'Unmatching product') {
    // tell user the key is for a different product; ask for the correct key
  }
  throw e
}

Prevention

When it happens

Trigger: activateLicense/checkLicense succeeds at the Lemonsqueezy layer, then `remoteConfig.product_ids.includes(json.meta.product_id)` is false. Common when a user pastes a license key bought for a different product/bundle.

Common situations: User owns multiple Lemonsqueezy products and pastes the wrong key; remote config product_ids was updated but the user's cached key is from a legacy product; a resold/shared key is for a different SKU.

Related errors


AI-assisted analysis of chatboxai/chatbox@81571269ad (2026-08-12). Data as JSON: /api/errors/9bea14a28bf5a6c9. Report an issue: GitHub.