coreyhaines31/marketingskills · error · Error
ZOOMINFO_USERNAME and ZOOMINFO_PRIVATE_KEY required for auth
Error message
ZOOMINFO_USERNAME and ZOOMINFO_PRIVATE_KEY required for authentication
What it means
ZoomInfo's authenticate() requires both ZOOMINFO_USERNAME and ZOOMINFO_PRIVATE_KEY when no pre-supplied ZOOMINFO_ACCESS_TOKEN exists. This in-function guard is reachable because the top-level startup guard only checks for ZOOMINFO_USERNAME (not ZOOMINFO_PRIVATE_KEY), so the process passes startup validation and then fails on the first API call.
Source
Thrown at tools/clis/zoominfo.js:17
#!/usr/bin/env node
const BASE_URL = 'https://api.zoominfo.com'
let ACCESS_TOKEN = process.env.ZOOMINFO_ACCESS_TOKEN
if (!ACCESS_TOKEN && !process.env.ZOOMINFO_USERNAME) {
console.error(JSON.stringify({ error: 'ZOOMINFO_ACCESS_TOKEN or ZOOMINFO_USERNAME + ZOOMINFO_PRIVATE_KEY environment variables required' }))
process.exit(1)
}
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}`)View on GitHub (pinned to 7868cb9251)
Solutions
- Set both ZOOMINFO_USERNAME and ZOOMINFO_PRIVATE_KEY in the environment (or supply ZOOMINFO_ACCESS_TOKEN directly to skip the authenticate() path).
- Tighten the top-level guard to require the pair together so the CLI fails fast at startup instead of mid-run: `if (!ACCESS_TOKEN && (!process.env.ZOOMINFO_USERNAME || !process.env.ZOOMINFO_PRIVATE_KEY))`.
- Verify with `node -e "console.log(!!process.env.ZOOMINFO_USERNAME, !!process.env.ZOOMINFO_PRIVATE_KEY)"` before running the command.
Example fix
// before
if (!ACCESS_TOKEN && !process.env.ZOOMINFO_USERNAME) {
console.error(JSON.stringify({ error: 'ZOOMINFO_ACCESS_TOKEN or ZOOMINFO_USERNAME + ZOOMINFO_PRIVATE_KEY environment variables required' }))
process.exit(1)
}
// after
if (!ACCESS_TOKEN && (!process.env.ZOOMINFO_USERNAME || !process.env.ZOOMINFO_PRIVATE_KEY)) {
console.error(JSON.stringify({ error: 'ZOOMINFO_ACCESS_TOKEN or ZOOMINFO_USERNAME + ZOOMINFO_PRIVATE_KEY environment variables required' }))
process.exit(1)
} Defensive patterns
Strategy: validation
Validate before calling
function validateZoominfoCreds() {
const token = process.env.ZOOMINFO_ACCESS_TOKEN
const user = process.env.ZOOMINFO_USERNAME
const key = process.env.ZOOMINFO_PRIVATE_KEY
if (token) return
if (!user || !key) {
const missing = [!user && 'ZOOMINFO_USERNAME', !key && 'ZOOMINFO_PRIVATE_KEY'].filter(Boolean)
throw new Error(`ZoomInfo requires ACCESS_TOKEN or both USERNAME+PRIVATE_KEY. Missing: ${missing.join(', ')}`)
}
} Prevention
- Always set ZOOMINFO_USERNAME and ZOOMINFO_PRIVATE_KEY as a pair, or set ZOOMINFO_ACCESS_TOKEN alone.
- Store the private key in a file and export via `export ZOOMINFO_PRIVATE_KEY="$(cat key.pem)"` to avoid newline corruption.
- Run `node -e "console.log(!!process.env.ZOOMINFO_USERNAME, !!process.env.ZOOMINFO_PRIVATE_KEY)"` before the first call.
When it happens
Trigger: ZOOMINFO_ACCESS_TOKEN is unset, ZOOMINFO_USERNAME is set, but ZOOMINFO_PRIVATE_KEY is unset or empty -- authenticate() proceeds past the ACCESS_TOKEN check and hits this throw before any network call.
Common situations: Partial credentials pasted into .env (username only), ZOOMINFO_PRIVATE_KEY line-wrapped or truncated by the shell, a CI secret configured for username but not the private key, or copy-paste of the public-style username without its paired key.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Failed to obtain access token
- Failed to obtain access token
- Authentication failed (${res.status}): ${text}
- No JWT in response
- Authentication failed: ${text}
AI-assisted analysis of coreyhaines31/marketingskills@7868cb9251 (2026-08-13).
Data as JSON: /api/errors/61ee5e9820b33e7b.
Report an issue: GitHub.