hcengineering/platform · error

Failed to ensure person exists for email: ${email}

Error message

Failed to ensure person exists for email: ${email}

What it means

ensurePerson resolves (or creates and caches) a Person record for an email address. If the cached/returned promise resolves to undefined — the fetch-and-cache path found or produced no person — this Error is thrown because subsequent mail processing requires a concrete Person (socialId).

Source

Thrown at services/mail/mail-common/src/person.ts:55

    private readonly restClient: RestClient
  ) {}

  /**
   * Gets or creates a person by email address with caching
   */
  async ensurePerson (contact: EmailContact): Promise<CachedPerson> {
    const email = contact.email.toLowerCase().trim()

    let personPromise = this.cache.get(email)

    if (personPromise === undefined) {
      personPromise = this.fetchAndCachePerson(email, contact.firstName, contact.lastName)
      this.cache.set(email, personPromise)
    }

    const result = await personPromise
    if (result === undefined) {
      throw new Error(`Failed to ensure person exists for email: ${email}`)
    }
    this.emailCache.set(result.socialId, email)
    return result
  }

  async getEmailBySocialId (socialId: PersonId): Promise<string | undefined> {
    return this.emailCache.get(socialId)
  }

  size (): number {
    return this.cache.size
  }

  clearCache (): void {
    this.cache.clear()
    this.emailCache.clear()
  }

View on GitHub (pinned to 63e28dc964)

Solutions

  1. Inspect why fetchAndCachePerson returned undefined — check DB errors and whether person creation is permitted in the workspace.
  2. Validate the email is non-empty and well-formed before calling ensurePerson (see also 'Invalid email address').
  3. Catch the error and skip/queue the message for retry instead of failing the whole ingestion pipeline.
  4. Check for creation races; make fetchAndCachePerson's upsert idempotent and always return the person.

Example fix

// before
const person = await service.ensurePerson(ctx, email) // throws if undefined
// after
if (!email || !email.includes('@')) {
  throw new Error(`Skipping message: bad sender email "${email}"`)
}
const person = await service.ensurePerson(ctx, email)
Defensive patterns

Strategy: try-catch

Validate before calling

if (typeof email !== 'string' || !email.includes('@')) {
  throw new Error(`ensurePerson requires a valid email, got: ${email}`)
}

Type guard

function isValidEmail(email: string): boolean {
  return /^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(email)
}

Try / catch

try {
  person = await service.ensurePerson(ctx, email)
} catch (err) {
  if (err.message.startsWith('Failed to ensure person exists')) {
    console.warn(`Skipping message from unresolvable person: ${email}`)
    return // or queue for retry
  }
  throw err
}

Prevention

When it happens

Trigger: fetchAndCachePerson returning undefined for the email (creation failed silently or lookup returned nothing) while processing message sender/recipient (fromPerson/toPerson) records.

Common situations: Emails from malformed or empty addresses slipping through upstream validation; concurrent processing racing person creation so one path resolves nothing; database issue during person upsert; contact with blank firstName/lastName plus a lookup-only path.

Related errors


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