{"record":{"id":"61ee5e9820b33e7b","repo":"coreyhaines31/marketingskills","slug":"zoominfo-username-and-zoominfo-private-key-require","errorCode":null,"errorMessage":"ZOOMINFO_USERNAME and ZOOMINFO_PRIVATE_KEY required for authentication","messagePattern":"ZOOMINFO_USERNAME and ZOOMINFO_PRIVATE_KEY required for authentication","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"tools/clis/zoominfo.js","lineNumber":17,"sourceCode":"#!/usr/bin/env node\n\nconst BASE_URL = 'https://api.zoominfo.com'\n\nlet ACCESS_TOKEN = process.env.ZOOMINFO_ACCESS_TOKEN\n\nif (!ACCESS_TOKEN && !process.env.ZOOMINFO_USERNAME) {\n  console.error(JSON.stringify({ error: 'ZOOMINFO_ACCESS_TOKEN or ZOOMINFO_USERNAME + ZOOMINFO_PRIVATE_KEY environment variables required' }))\n  process.exit(1)\n}\n\nasync function authenticate() {\n  if (ACCESS_TOKEN) return ACCESS_TOKEN\n  const username = process.env.ZOOMINFO_USERNAME\n  const password = process.env.ZOOMINFO_PRIVATE_KEY\n  if (!username || !password) {\n    throw new Error('ZOOMINFO_USERNAME and ZOOMINFO_PRIVATE_KEY required for authentication')\n  }\n  const res = await fetch(`${BASE_URL}/authenticate`, {\n    method: 'POST',\n    headers: { 'Content-Type': 'application/json' },\n    body: JSON.stringify({ username, password }),\n  })\n  const text = await res.text()\n  if (!res.ok) {\n    throw new Error(`Authentication failed (${res.status}): ${text}`)\n  }\n  try {\n    const data = JSON.parse(text)\n    if (!data.jwt) throw new Error('No JWT in response')\n    ACCESS_TOKEN = data.jwt\n    return ACCESS_TOKEN\n  } catch (e) {\n    if (e.message === 'No JWT in response') throw e\n    throw new Error(`Authentication failed: ${text}`)","sourceCodeStart":1,"sourceCodeEnd":35,"githubUrl":"https://github.com/coreyhaines31/marketingskills/blob/7868cb9251fad80a73d26e488a5ad5f6c4a9f335/tools/clis/zoominfo.js#L1-L35","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nif (!ACCESS_TOKEN && !process.env.ZOOMINFO_USERNAME) {\n  console.error(JSON.stringify({ error: 'ZOOMINFO_ACCESS_TOKEN or ZOOMINFO_USERNAME + ZOOMINFO_PRIVATE_KEY environment variables required' }))\n  process.exit(1)\n}\n\n// after\nif (!ACCESS_TOKEN && (!process.env.ZOOMINFO_USERNAME || !process.env.ZOOMINFO_PRIVATE_KEY)) {\n  console.error(JSON.stringify({ error: 'ZOOMINFO_ACCESS_TOKEN or ZOOMINFO_USERNAME + ZOOMINFO_PRIVATE_KEY environment variables required' }))\n  process.exit(1)\n}","handlingStrategy":"validation","validationCode":"function validateZoominfoCreds() {\n  const token = process.env.ZOOMINFO_ACCESS_TOKEN\n  const user = process.env.ZOOMINFO_USERNAME\n  const key = process.env.ZOOMINFO_PRIVATE_KEY\n  if (token) return\n  if (!user || !key) {\n    const missing = [!user && 'ZOOMINFO_USERNAME', !key && 'ZOOMINFO_PRIVATE_KEY'].filter(Boolean)\n    throw new Error(`ZoomInfo requires ACCESS_TOKEN or both USERNAME+PRIVATE_KEY. Missing: ${missing.join(', ')}`)\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["authentication","configuration","zoominfo","validation"],"backgroundTag":null,"analyzedSha":"7868cb9251fad80a73d26e488a5ad5f6c4a9f335","analyzedAt":"2026-08-13T03:33:17.303Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}