hcengineering/platform · warning

'text' and 'html' are missing

Error message

'text' and 'html' are missing

What it means

handleSendMail returns HTTP 400 { err: "'text' and 'html' are missing" } when neither text nor html is present in the request body. The mail service requires at least one body representation for the email and refuses the request before contacting the mail backend.

Source

Thrown at services/mail/pod-mail/src/main.ts:97

export async function handleSendMail (
  client: MailClient,
  req: Request,
  res: Response,
  ctx: MeasureContext
): Promise<void> {
  const { from, to, subject, text, html, attachments, headers, apiKey, password } = req.body
  if (process.env.API_KEY !== undefined && process.env.API_KEY !== apiKey) {
    ctx.warn('Unauthorized access attempt to send email', {
      from,
      to
    })
    res.status(401).send({ err: 'Unauthorized' })
    return
  }
  const fromAddress = from ?? config.source
  if (text === undefined && html === undefined) {
    ctx.warn('Text and html are missing in email request', { from, to })
    res.status(400).send({ err: "'text' and 'html' are missing" })
    return
  }
  if (subject === undefined) {
    ctx.warn('Subject is missing in email request', { from, to })
    res.status(400).send({ err: "'subject' is missing" })
    return
  }
  if (to === undefined) {
    ctx.warn('To address is missing in email request', { from })
    res.status(400).send({ err: "'to' is missing" })
    return
  }
  if (fromAddress === undefined) {
    ctx.warn('From address is missing in email request', { to })
    res.status(400).send({ err: "'from' is missing" })
    return
  }
  const message: SendMailOptions = {

View on GitHub (pinned to 63e28dc964)

Solutions

  1. Include at least one of text or html in the request body.
  2. Check for typos/field renaming in the client payload (text, html must be top-level body fields).
  3. Default to a non-empty fallback body when the generated content is empty.

Example fix

// before
const body = { to, subject } // 400: text and html missing
// after
const body = { to, subject, text: text ?? '', html }
if (body.text === '' && body.html === undefined) throw new Error('email body required')
Defensive patterns

Strategy: validation

Validate before calling

function assertMailBody(m: { text?: string; html?: string }): void {
  if (m.text === undefined && m.html === undefined) {
    throw new Error("email requires at least one of 'text' or 'html'")
  }
}

Type guard

function hasEmailBody(m: { text?: unknown; html?: unknown }): boolean {
  return m.text !== undefined || m.html !== undefined
}

Try / catch

assertMailBody(mail)
const res = await fetch(mailUrl, { method: 'POST', body: JSON.stringify(mail) })
if (res.status === 400) {
  const { err } = await res.json()
  throw new Error(`mail rejected: ${err}`)
}

Prevention

When it happens

Trigger: POST to the send-mail endpoint with a body lacking both text and html fields — e.g. { to, subject } only, or fields misspelled (body/message instead of text), or an empty JSON body.

Common situations: Building the payload dynamically and skipping body fields when content is empty; sending a template id instead of rendered content; an API client wrapper that drops undefined fields, leaving neither text nor html.

Related errors


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