hcengineering/platform · error · ApiError

Person not found

Error message

Person not found

What it means

ApiError(404, 'Person not found') is thrown when getAccountPerson(token.account) returns undefined during the telegram-bot token exchange. The account is authenticated but no Person record is associated with it in the workspace, so the flow cannot proceed and the server responds with HTTP 404.

Source

Thrown at services/telegram-bot/pod-telegram-bot/src/server.ts:119

      if (req.body == null || typeof req.body !== 'object') {
        throw new ApiError(400)
      }

      const { code } = req.body

      if (code == null || code === '' || typeof code !== 'string') {
        throw new ApiError(400)
      }

      const integration = await getAnyIntegrationByAccount(token.account)

      if (integration !== undefined) {
        throw new ApiError(409, 'User already authorized')
      }

      const person = await getAccountPerson(token.account)
      if (person === undefined) {
        throw new ApiError(404, 'Person not found')
      }

      const newRecord = await worker.authorizeUser(code, token.account, token.workspace)
      if (newRecord === undefined) {
        throw new ApiError(500)
      }

      void worker.limiter.add(newRecord.telegramId, async () => {
        ctx.info('Connected account', { account: token.account, username: newRecord.username })
        const message = await translate(telegram.string.AccountConnectedHtml, {
          app: config.App,
          name: `${person.firstName} ${person.lastName}`
        })
        await bot.telegram.sendMessage(newRecord.telegramId, message, { parse_mode: 'HTML' })
      })

      res.status(200)
      res.json({})

View on GitHub (pinned to 63e28dc964)

Solutions

  1. Verify the account has a corresponding Person record in the target workspace and create/assign one if missing.
  2. Check that token.account and token.workspace are consistent — the token may have been issued for the wrong workspace.
  3. Re-run the account provisioning/onboarding step that links account to person, then retry authorization.
  4. Change the response to a client-actionable error explaining that the account profile must be completed first.

Example fix

// before
const person = await getAccountPerson(token.account)
if (person === undefined) {
  throw new ApiError(404, 'Person not found')
}
// after
let person = await getAccountPerson(token.account)
if (person === undefined) {
  person = await ensurePersonForAccount(token.account, token.workspace)
}
Defensive patterns

Strategy: validation

Validate before calling

const person = await getAccountPerson(account)
if (person === undefined) {
  throw new Error('Account has no linked Person; complete profile setup before connecting Telegram')
}

Type guard

function isPersonNotFound(err: unknown): err is ApiError {
  return err instanceof ApiError && (err as ApiError).message === 'Person not found'
}

Try / catch

try {
  await exchangeToken(token, code)
} catch (err) {
  if (err instanceof ApiError && err.message === 'Person not found') {
    redirectToProfileSetup()
  } else throw err
}

Prevention

When it happens

Trigger: The exchanging token's account exists but has no linked Person document in the target workspace (token.workspace), e.g. the account was created without person assignment or the person record was deleted.

Common situations: Service/guest accounts created outside the normal signup flow, workspaces where person records were removed or not synced, or a token issued for an account in a different workspace than where the person lives.

Related errors


AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29). Data as JSON: /api/errors/571ee707f8a0f5c7. Report an issue: GitHub.