{"record":{"id":"bef3609cadd3aa31","repo":"hcengineering/platform","slug":"invalid-tier-id-tierid","errorCode":null,"errorMessage":"Invalid tier id: ${tierId}","messagePattern":"Invalid tier id: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugins/billing-resources/src/components/Subscriptions.svelte","lineNumber":384,"sourceCode":"      } else {\n        // Subscription already exists and matches this checkout, just clean up the URL\n        const cleanedLoc = { ...loc, query: {} }\n        navigate(cleanedLoc)\n      }\n    }\n  }\n\n  $: isCheckoutPolling = pollingCheckoutId !== null\n\n  function formatEndDate (endDate: number): string {\n    const date = new Date(endDate)\n    return date.toLocaleDateString(undefined, { year: 'numeric', month: 'long', day: 'numeric' })\n  }\n\n  function getTypeAndPlan (tierId: Ref<Tier>): { type: SubscriptionType, plan: string } {\n    const parts = tierId.split(':')\n    if (parts.length !== 3) {\n      throw new Error(`Invalid tier id: ${tierId}`)\n    }\n\n    return {\n      type: parts[1] as SubscriptionType,\n      plan: parts[2].toLowerCase()\n    }\n  }\n\n  onMount(() => {\n    void (async () => {\n      // First, load current subscriptions\n      await fetchSubscriptions()\n\n      // Then fetch usage stats\n      await fetchUsageStats()\n\n      // Then check if we need to poll for a new subscription from checkout\n      checkForCheckoutParam()","sourceCodeStart":366,"sourceCodeEnd":402,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/plugins/billing-resources/src/components/Subscriptions.svelte#L366-L402","documentation":"getTypeAndPlan in Subscriptions.svelte parses a tier id expected in the form '<prefix>:<type>:<plan>' (three colon-separated parts). If tierId.split(':') does not yield exactly 3 parts it throws `Invalid tier id: ${tierId}`. The tier id came from data that doesn't match the expected naming convention.","triggerScenarios":"Rendering a subscription whose tier Ref<Tier> is not of the canonical 'xxx:type:plan' form — e.g. a default/free tier, a manually created tier, or a tier id produced by a different/older version of the billing plugin.","commonSituations":"Environments where tiers were created before the tier-id naming convention was introduced, custom tiers made by admins, or test/staging data copied between environments with mismatched tier formats.","solutions":["Inspect the offending tierId string and re-create the tier using the current '<prefix>:<type>:<plan>' naming convention.","Add a defensive branch returning a sensible default (e.g. { type: 'subscription', plan: 'free' }) for ids that don't match.","Migrate legacy tier ids in the database to the 3-part format.","Align plugin versions so tier creation and tier parsing use the same id format."],"exampleFix":"// before\nconst parts = tierId.split(':')\nif (parts.length !== 3) {\n  throw new Error(`Invalid tier id: ${tierId}`)\n}\n// after\nconst parts = tierId.split(':')\nif (parts.length !== 3) {\n  console.warn('Non-standard tier id, using defaults:', tierId)\n  return { type: 'subscription' as SubscriptionType, plan: 'free' }\n}","handlingStrategy":"type-guard","validationCode":"function isCanonicalTierId(tierId: string): boolean {\n  return tierId.split(':').length === 3\n}\n// before rendering:\nif (!isCanonicalTierId(subscription.tier)) renderFallbackTier(subscription)","typeGuard":"function parseTierId(tierId: string): { type: SubscriptionType, plan: string } | null {\n  const parts = tierId.split(':')\n  return parts.length === 3\n    ? { type: parts[1] as SubscriptionType, plan: parts[2].toLowerCase() }\n    : null\n}","tryCatchPattern":"try {\n  const { type, plan } = getTypeAndPlan(tierId)\n  renderSubscription(type, plan)\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith('Invalid tier id')) {\n    renderFallbackSubscription(tierId) // show raw id or 'unknown plan'\n  } else throw err\n}","preventionTips":["Create tiers only through the billing plugin so ids follow the '<prefix>:<type>:<plan>' format.","Migrate legacy tier ids after upgrading the plugin.","Add a parsing helper returning null instead of throwing for display code."],"tags":["billing","parsing","data-format","subscriptions"],"backgroundTag":"invalid-identifier-format","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}