ScreenshotNeo

BlogHow-to

How to Fix Happo Authentication Errors in GitHub Actions

Trace Happo authentication failures to the right credential boundary, check secret scope and workflow wiring, and fix the configuration without exposing secrets.

By the ScreenshotNeo team4 October 20268 min read

To fix a Happo authentication error in GitHub Actions, first identify which command and service returned it. Happo needs its own API key and secret; GitHub’s GITHUB_TOKEN is for GitHub API access and does not replace Happo credentials. Confirm the Happo configuration reads the expected environment variables, then map the matching GitHub secrets into the environment of the step that runs Happo.

The error alone does not identify the cause. A 401 from Happo points to Happo authentication; a 401 or 403 from a GitHub API call points to that call’s token or permissions. Use the response, failing command, client version, workflow event, and secret scope to narrow it down before changing credentials.

1. Find the failing authentication boundary

Start with the log line for the first failing command, not just the final workflow summary. Record the command or action, its version, the destination host if shown, the HTTP status and redacted response. Do not copy a live token or secret into a support ticket or issue.

What failed Credential to inspect What to check
Happo CLI or a Happo action Happo API key and secret Happo config, variable names, secret values and availability to the job
A request to the GitHub API GITHUB_TOKEN or a GitHub App token Token permissions, repository scope and whether the resource is in the workflow repository
A custom request directly to Happo’s API Happo API key and secret Whether the request uses a documented Happo authentication format

Happo’s API supports HTTP Basic authentication with the key and secret together, or JWT authentication generated using the key and secret. This matters when debugging a direct API integration. For normal CLI configuration, use the authentication method expected by the installed client; do not change the protocol based on guesswork. Happo API authentication reference.

2. Match Happo configuration to GitHub secrets

The Happo npm package example reads the API credentials from HAPPO_API_KEY and HAPPO_API_SECRET and assigns them to the apiKey and apiSecret configuration values. Confirm that your project’s actual Happo configuration follows the same names, or update the workflow mapping to match your configuration.

// happo.config.js (CommonJS)
module.exports = {
  apiKey: process.env.HAPPO_API_KEY,
  apiSecret: process.env.HAPPO_API_SECRET,
};

Store the values in GitHub repository, organization, or environment secrets, then pass them to the step that invokes Happo. This example assumes that the secret names in GitHub are exactly HAPPO_API_KEY and HAPPO_API_SECRET.

name: Visual tests
on: [push]

jobs:
  happo:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - run: npm ci
      - name: Run Happo
        env:
          HAPPO_API_KEY: ${{ secrets.HAPPO_API_KEY }}
          HAPPO_API_SECRET: ${{ secrets.HAPPO_API_SECRET }}
        run: npx happo

The configuration file and workflow example show the expected wiring; adapt the command, package installation and Node version to the project. The key point is that the environment variables must be present in the step that actually runs Happo. Mapping them at a different job or step does not make them available everywhere automatically.

Check variable presence without printing their values. For example, a shell check can report whether both variables are set:

- name: Check Happo credential availability
  env:
    HAPPO_API_KEY: ${{ secrets.HAPPO_API_KEY }}
    HAPPO_API_SECRET: ${{ secrets.HAPPO_API_SECRET }}
  run: |
    test -n "$HAPPO_API_KEY" || { echo "HAPPO_API_KEY is missing"; exit 1; }
    test -n "$HAPPO_API_SECRET" || { echo "HAPPO_API_SECRET is missing"; exit 1; }

This check reveals whether the variables are empty without exposing credentials. Avoid commands such as echo "$HAPPO_API_SECRET", even for debugging.

3. Check secret scope, timing and workflow event

A secret existing in GitHub settings does not prove it was available to the failing run. Repository and organization secrets are read when a run is queued. Environment secrets are read when a job that references that environment starts. If the secrets are environment-scoped, declare the same environment on the job:

jobs:
  happo:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - name: Run Happo
        env:
          HAPPO_API_KEY: ${{ secrets.HAPPO_API_KEY }}
          HAPPO_API_SECRET: ${{ secrets.HAPPO_API_SECRET }}
        run: npx happo

Replace production with the exact GitHub environment that contains the credentials. GitHub documents that an environment-level secret takes precedence when a secret with the same name also exists at the repository or organization level. Verify the value at the winning scope if a recent change did not affect the run.

Also check the workflow trigger and the repository context for the run. Secret availability depends on the event and security context. In particular, do not assume a pull request from a fork can read ordinary repository secrets. Review GitHub’s current guidance on using secrets in Actions for the applicable event. Do not work around unavailable secrets by exposing them to untrusted pull-request code.

4. Diagnose direct Happo API authentication

Most workflows should let the Happo CLI or package handle authentication from configuration. If your failing step makes a direct Happo API request, compare the request with the API’s documented formats:

  • Basic: send Authorization: Basic <base64(apiKey:apiSecret)>.
  • JWT: sign a token using the API secret, include {"key":"<your API key>"} as the payload, set the JWT header’s kid to the API key, and send the token as Authorization: Bearer <token>.

Use the precise request format and endpoint required by the Happo API operation. Do not print the generated Basic value or JWT in logs: both are credentials. If a direct request gets a Happo 401, check that the pair belongs together, is current, and is encoded and sent in the format the request expects.

5. Keep GitHub API credentials separate

If a later workflow step posts a comment, reads a check, or calls another GitHub API, troubleshoot that step’s token separately. GitHub recommends its built-in GITHUB_TOKEN where possible, but documents that this token can access only resources within the workflow’s repository. Check the workflow’s token permissions and the target repository; a GitHub App may be needed when the workflow must access other resources. See GitHub’s automatic token authentication guide.

Do not replace HAPPO_API_KEY or HAPPO_API_SECRET with GITHUB_TOKEN. They authenticate to different services.

6. Common errors and fixes

Symptom Likely cause Fix
Happo reports missing credentials The variables are not mapped into the Happo step, the names differ, or the selected secret is empty. Compare the config variable names with the workflow’s env keys and check presence without printing values.
Happo returns 401 The key or secret is wrong, the pair does not match, the secret is stale, or a direct request uses the wrong authentication format. Verify the values and scope in GitHub settings, rotate if needed, and confirm the client or request follows Happo’s current authentication documentation.
It works on pushes but fails on a pull request The event or repository context may not provide the secrets to that run. Check the event’s secret-availability rules and use a workflow design that does not expose credentials to untrusted code.
A secret update appears to have no effect The value may be defined at more than one scope; the environment-level value takes precedence over repository or organization values. Inspect the scope used by the job and update the winning secret. Queue a fresh run after updating repository or organization secrets.
GitHub API returns 401 or 403 The failure is at GitHub’s token or permission boundary, not Happo authentication. Check GITHUB_TOKEN permissions, target repository scope and whether the workflow needs a GitHub App token.
Authentication changed after a dependency update The installed Happo CLI or package version may differ from the version assumed by the configuration. Record the installed version, check the current Happo documentation and changelog, and confirm the configuration format for that client version.

7. Rotate credentials and escalate safely

  1. If a secret may have been exposed or pasted incorrectly, rotate the Happo API secret using the appropriate Happo account controls.
  2. Update the corresponding GitHub secret at the scope the job uses. Check for duplicates at environment, repository and organization levels.
  3. Start a new run and verify only whether the expected variables are present. Never include the credential values in logs.
  4. If it still fails, send Happo support the redacted failing command, exact status and response, client version, workflow trigger, and confirmation of which secret scope the job uses. Happo lists support@happo.io for technical support.

Happo’s changelog records an “Improved CLI authentication” entry in version 17.21 (December 5, 2025) and “Clearer job failures” in version 17.145 (September 3, 2026). These entries make the installed version and current failure output useful diagnostic details; neither establishes the cause of an individual workflow error. See the Happo changelog.

8. Or skip the browser setup

This Happo issue is about CI credentials. If the job also needs website screenshots, ScreenshotNeo provides a screenshot API and MCP server for developers. One GET request can return a PNG, JPEG, WebP or PDF. See the ScreenshotNeo API documentation.

curl -G "https://api.screenshotneo.com/v1/shot" \
  -d access_key=YOUR_API_KEY \
  --data-urlencode url=https://stripe.com \
  -o shot.webp

Python:

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)

Node.js:

const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://stripe.com',
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`ScreenshotNeo returned ${res.status}`);
await Bun.write('shot.webp', res);

ScreenshotNeo accepts cookie and consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups and chat widgets before capture; each step can be turned off. Bot checks, blank pages, timeouts, failed loads and cache hits cost nothing, and responses identify the page verdict and billing status in headers. Its MCP server offers take_screenshot, get_page_info and capture_pdf for Claude, Cursor and other MCP clients. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Every feature is on every plan. Sign up for ScreenshotNeo’s free plan.

FAQ

Can I use GITHUB_TOKEN as my Happo API key?

No. Happo API credentials authenticate to Happo. GITHUB_TOKEN authenticates GitHub API requests.

Should I switch Happo from Basic auth to JWT?

Only if the failing direct API request or client setup requires that format. First identify the failing command and check its supported configuration.

What details should I provide when asking for help?

Share the redacted command, exact error and status, client version, workflow event and secret scope. Do not share the API key, secret, generated token or encoded credentials.