{"record":{"id":"99f80f8d1be2ef70","repo":"ionic-team/ionic-framework","slug":"side-is-not-a-valid-value-for-side-use-st","errorCode":null,"errorMessage":"\"${side}\" is not a valid value for [side]. Use \"start\" or \"end\" instead.","messagePattern":"\"(.+?)\" is not a valid value for \\[side\\]\\. Use \"start\" or \"end\" instead\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"core/src/utils/helpers.ts","lineNumber":332,"sourceCode":"  return { x: 0, y: 0 };\n};\n\n/**\n * @hidden\n * Given a side, return if it should be on the end\n * based on the value of dir\n * @param side the side\n * @param isRTL whether the application dir is rtl\n */\nexport const isEndSide = (side: Side): boolean => {\n  const isRTL = document.dir === 'rtl';\n  switch (side) {\n    case 'start':\n      return isRTL;\n    case 'end':\n      return !isRTL;\n    default:\n      throw new Error(`\"${side}\" is not a valid value for [side]. Use \"start\" or \"end\" instead.`);\n  }\n};\n\nexport const deferEvent = (event: EventEmitter): EventEmitter => {\n  return debounceEvent(event, 0);\n};\n\nexport const debounceEvent = (event: EventEmitter, wait: number): EventEmitter => {\n  const original = (event as any)._original || event;\n  return {\n    _original: event,\n    emit: debounce(original.emit.bind(original), wait),\n  } as EventEmitter;\n};\n\nexport const debounce = (func: (...args: any[]) => void, wait = 0) => {\n  let timer: any;\n  return (...args: any[]): any => {","sourceCodeStart":314,"sourceCodeEnd":350,"githubUrl":"https://github.com/ionic-team/ionic-framework/blob/625f9c38ad8c5db8a6c12df5c1622a36da90f9e3/core/src/utils/helpers.ts#L314-L350","documentation":"Thrown by isEndSide() in helpers.ts when the `side` argument is neither 'start' nor 'end'. isEndSide maps a logical side to a physical one based on document.dir (RTL), and only the two logical sides are meaningful. The Side type already restricts the value at compile time, so a runtime hit means the type was bypassed.","triggerScenarios":"isEndSide is called with a value outside the union - e.g. 'left', 'right', an empty string, or undefined - typically because a component prop was bound to an unvalidated string or cast with `as Side` / `as any`.","commonSituations":"Binding [side] on ion-menu or ion-item-options to a free-form string; reading side from a config/URL and forwarding it without validation; third-party wrappers that accept 'left'/'right' and pass them through.","solutions":["Only ever assign 'start' or 'end' to side props; map 'left'/'right' in your own code before binding.","Validate user-supplied side values against ['start','end'] and default to 'start'.","Remove `as Side` / `as any` casts on side and let TypeScript enforce the union.","If you control the data source, store sides as the literal union."],"exampleFix":"// before\nconst side = (userPref as any) as Side; // 'left' slips through\nisEndSide(side);\n\n// after\nconst side: Side = userPref === 'end' ? 'end' : 'start';\nisEndSide(side);","handlingStrategy":"type-guard","validationCode":"const VALID_SIDES = ['start','end'] as const;\nfunction normalizeSide(s: unknown): Side {\n  return s === 'end' ? 'end' : 'start';\n}","typeGuard":"function isSide(v: unknown): v is Side {\n  return v === 'start' || v === 'end';\n}","tryCatchPattern":null,"preventionTips":["Never bind side to a free-form string; map 'left'/'right' yourself.","Remove `as Side` / `as any` casts on side.","Store side values as the literal union in your data model."],"tags":["menu","side","rtl","validation","runtime"],"backgroundTag":null,"analyzedSha":"625f9c38ad8c5db8a6c12df5c1622a36da90f9e3","analyzedAt":"2026-08-12T16:00:25.413Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}