{"record":{"id":"dbcd668b8147a130","repo":"hcengineering/platform","slug":"email-is-not-defined-in-integration","errorCode":null,"errorMessage":"Email is not defined in integration","messagePattern":"Email is not defined in integration","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugins/calendar-resources/src/api.ts","lineNumber":37,"sourceCode":"import { type Integration } from '@hcengineering/account-client'\nimport {\n  type IntegrationClient,\n  getIntegrationClient as getIntegrationClientRaw,\n  request\n} from '@hcengineering/integration-client'\nimport calendar from './plugin'\nimport { calendarIntegrationKind } from '@hcengineering/calendar'\n\nexport async function signout (integration: Integration, client: IntegrationClient): Promise<void> {\n  const url = getMetadata(calendar.metadata.CalendarServiceURL)\n  const token = getMetadata(presentation.metadata.Token)\n  if (url === undefined || token === undefined) {\n    throw new Error('Calendar service URL or token is not defined')\n  }\n  const connection = await client.getConnection(integration)\n  const email = integration.data?.email ?? connection?.data?.email\n  if (email === undefined) {\n    throw new Error('Email is not defined in integration')\n  }\n  await request({\n    baseUrl: url,\n    path: `/signout?value=${email}`,\n    method: 'GET',\n    token\n  })\n}\n\nexport async function disconnect (integration: Integration): Promise<void> {\n  const integrationClient = await getIntegrationClient()\n  const result = await integrationClient.removeIntegration(integration.socialId, integration.workspaceUuid)\n  if (result !== undefined && result.connectionRemoved) {\n    await signout(integration, integrationClient)\n  }\n}\n\nexport async function disconnectAll (integration: Integration): Promise<void> {","sourceCodeStart":19,"sourceCodeEnd":55,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/plugins/calendar-resources/src/api.ts#L19-L55","documentation":"After validating service URL/token, signout determines the account email from integration.data?.email or the stored connection's email. If neither is present the service cannot be told which account to sign out, so this error is thrown.","triggerScenarios":"Calling signout for an Integration whose data.email is undefined and whose stored IntegrationConnection has no email — e.g. a partially created or corrupted calendar integration record.","commonSituations":"Integration created through a broken OAuth flow that never stored the email; manually seeded integration records without email; older integration records created before email was captured, hit during disconnectAll.","solutions":["Set the email on the integration data (re-run the connect/OAuth flow) before disconnecting","Check the connection record and add the missing email","Delete the broken integration record and reconnect the calendar account"],"exampleFix":"// before\nawait signout(integration, client) // throws: no email\n// after\nconst email = integration.data?.email ?? (await client.getConnection(integration))?.data?.email\nif (email !== undefined) {\n  await signout(integration, client)\n}","handlingStrategy":"type-guard","validationCode":"const conn = await client.getConnection(integration)\nconst email = integration.data?.email ?? conn?.data?.email\nif (email === undefined) { /* repair integration or skip signout */ }","typeGuard":"const hasEmail = (i: Integration, c?: IntegrationConnection): i is Integration & { data: { email: string } } =>\n  typeof (i.data?.email ?? c?.data?.email) === 'string'","tryCatchPattern":"try {\n  await signout(integration, client)\n} catch (err) {\n  if ((err as Error).message === 'Email is not defined in integration') {\n    await repairOrRecreateIntegration(integration) // reconnect flow to capture email\n  } else throw err\n}","preventionTips":["Always capture and store the account email during the connect/OAuth flow","Validate integration records include email before allowing them to be persisted","During disconnectAll, tolerate integrations missing email instead of failing the whole batch"],"tags":["integration","calendar","data-integrity"],"backgroundTag":"missing-integration-email","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}