{"record":{"id":"f3e5508d86649a72","repo":"tinyhumansai/openhuman","slug":"please-paste-a-meeting-link","errorCode":null,"errorMessage":"Please paste a meeting link.","messagePattern":"Please paste a meeting link\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"app/src/services/meetCallService.ts","lineNumber":228,"sourceCode":"  listenOnly?: boolean;\n};\n\ntype CoreBackendMeetJoinResponse = { ok: boolean; meet_url: string; platform: string };\n\n/**\n * Join a meeting via the backend's Recall.ai bot. Supports Google Meet,\n * Zoom, Microsoft Teams, and Webex.\n *\n * Calls the core RPC `openhuman.agent_meetings_join`, which emits `bot:join`\n * over the core's persistent Socket.IO connection to the backend. The backend\n * streams events back (`bot:reply`, `bot:harness`, `bot:transcript`, `bot:left`)\n * which the core bridges to the frontend as `agent_meetings:*` socket events.\n */\nexport async function joinMeetViaBackendBot(\n  input: BackendMeetJoinInput\n): Promise<{ meetUrl: string; platform: string }> {\n  const meetUrl = input.meetUrl.trim();\n  if (!meetUrl) throw new Error('Please paste a meeting link.');\n\n  // Dual-mascot slots (issue #4277), mapped to the backend's snake_case wire\n  // shape. Absent → backend falls back to `mascot_id`.\n  const slots = input.mascots?.filter(m => m.mascotId?.trim());\n  const mascots =\n    slots && slots.length > 0\n      ? slots.map(m => ({\n          mascot_id: m.mascotId.trim(),\n          name: m.name?.trim() || undefined,\n          voice_id: m.voiceId?.trim() || undefined,\n          rive_colors: mapRiveColors(m.riveColors),\n        }))\n      : undefined;\n\n  // Flow/state metadata only — no participant names, voices, or the meet URL.\n  log(\n    'backend bot join corr=%s dual=%s slots=%d singleMascot=%s riveColors=%s',\n    input.correlationId?.trim() || '-',","sourceCodeStart":210,"sourceCodeEnd":246,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/app/src/services/meetCallService.ts#L210-L246","documentation":"Client-side validation in `joinMeetViaBackendBot`: `input.meetUrl.trim()` is empty. Thrown before any RPC, so the core/backend never saw the request. It guards the subsequent `openhuman.agent_meetings_join` call, which requires a join URL for the Recall.ai bot. Note the sibling `joinMeetingViaMascotBot` throws a plain object for the same condition — a caller catching Errors only would miss that one, but this function throws a real `Error`.","triggerScenarios":"Join form submitted with an empty or whitespace-only URL field; UI wiring passes an uncontrolled input's initial value; a deep link / automation invokes the join flow without a URL parameter.","commonSituations":"Form submit handler not disabling the button until the field is non-empty; e2e tests driving the form with blank input; programmatic callers (flows) omitting meetUrl.","solutions":["Disable the Join button until `meetUrl.trim()` is non-empty","Validate supported URL shape earlier (meet.google.com / zoom.us / teams.microsoft.com / webex) for a better message","In catch handlers, show this message as a form-field hint, not a crash banner"],"exampleFix":"// before\n<button onClick={() => joinMeetViaBackendBot({ meetUrl, ... })}>Join</button>\n\n// after\n<button disabled={!meetUrl.trim()}\n        onClick={() => joinMeetViaBackendBot({ meetUrl, ... })}>Join</button>","handlingStrategy":"validation","validationCode":"const url = meetUrl.trim();\nif (!url) {\n  setFieldError('Please paste a meeting link.');\n  return;\n}\nawait joinMeetViaBackendBot({ ...input, meetUrl: url });","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Disable submit until the URL field has non-whitespace content","Prefer validating at the form layer with a specific field message over relying on the thrown error","Remember the mascot-bot sibling throws a plain object for the same condition — guard input before either call"],"tags":["validation","meet","form","recall-ai"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}