coreyhaines31/marketingskills · error · Error

No JWT in response

Error message

No JWT in response

What it means

The /authenticate call returned a 2xx status and the body parsed as valid JSON, but the expected jwt field is absent or falsy. ZoomInfo's success contract is { jwt: "..." }; reaching this throw means the response was success-shaped but did not conform -- typically API/version drift, a maintenance page with 200 status, or an account lacking JWT issuance permission.

Solutions

  1. Log the full parsed data object before throwing to see exactly what ZoomInfo returned.
  2. Confirm BASE_URL matches the account's correct ZoomInfo region/endpoint.
  3. Check ZoomInfo release notes or contact support for an authenticate-response schema change.

Example fix

// before
if (!data.jwt) throw new Error('No JWT in response')

// after
if (!data.jwt) throw new Error(`No JWT in response: ${JSON.stringify(data)}`)
Defensive patterns

Strategy: type-guard

Type guard

function isZoominfoAuthResponse(v) {
  return v != null && typeof v === 'object' && typeof v.jwt === 'string' && v.jwt.length > 0
}

Try / catch

const data = JSON.parse(text)
if (!isZoominfoAuthResponse(data)) {
  throw new Error(`Unexpected ZoomInfo auth response shape: ${JSON.stringify(data)}`)
}

Prevention

When it happens

Trigger: A 2xx response whose JSON lacks a jwt key: ZoomInfo API version change renaming the token field, BASE_URL pointed at a tenant/region that returns a different shape, a partial response during maintenance, or an account plan without JWT access.

Common situations: BASE_URL (https://api.zoominfo.com) redirected or pointed at the wrong region, a recent ZoomInfo release that altered the authenticate response, or a proxy returning a 200 with a wrapped/extra-keyed body.

Related errors


AI-assisted analysis of coreyhaines31/marketingskills@7868cb9251 (2026-08-13). Data as JSON: /api/errors/d7910cc9a2f54bbf. Report an issue: GitHub.

Appendix: source

Thrown at tools/clis/zoominfo.js:30

async function authenticate() {
  if (ACCESS_TOKEN) return ACCESS_TOKEN
  const username = process.env.ZOOMINFO_USERNAME
  const password = process.env.ZOOMINFO_PRIVATE_KEY
  if (!username || !password) {
    throw new Error('ZOOMINFO_USERNAME and ZOOMINFO_PRIVATE_KEY required for authentication')
  }
  const res = await fetch(`${BASE_URL}/authenticate`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ username, password }),
  })
  const text = await res.text()
  if (!res.ok) {
    throw new Error(`Authentication failed (${res.status}): ${text}`)
  }
  try {
    const data = JSON.parse(text)
    if (!data.jwt) throw new Error('No JWT in response')
    ACCESS_TOKEN = data.jwt
    return ACCESS_TOKEN
  } catch (e) {
    if (e.message === 'No JWT in response') throw e
    throw new Error(`Authentication failed: ${text}`)
  }
}

async function api(method, path, body) {
  if (args['dry-run']) {
    return { _dry_run: true, method, url: `${BASE_URL}${path}`, headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ***' }, body }
  }
  const token = await authenticate()
  const res = await fetch(`${BASE_URL}${path}`, {
    method,
    headers: {
      'Content-Type': 'application/json',
      Authorization: `Bearer ${token}`,

View on GitHub (pinned to 7868cb9251)