Skip to content

API Authentication & Connection Debugging

This guide provides a systematic approach to debugging API authentication issues when developing pets. Follow this workflow when a pet's API connection fails.

Quick Debugging Checklist

  1. Test credentials directly with curl (bypass the plugin entirely)
  2. Check environment variable names match between .env and code
  3. Verify authentication method (Basic, Bearer, API Key header)
  4. Check for response compression (use --compressed with curl)
  5. Inspect error responses for clues (401, 403, validation errors)

Step-by-Step Debugging Workflow

Step 1: Isolate the Problem - Test with curl

Before debugging plugin code, verify the credentials work at all. Always test the API directly with curl first.

bash
# Load env vars and test - keeps credentials out of terminal history
source .env && curl -s --compressed \
  -u "${API_USERNAME}:${API_PASSWORD}" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  "https://api.example.com/endpoint"

Why source from .env?

  • Keeps credentials out of terminal history
  • Tests the exact same values your code will use
  • Reveals env var naming mismatches immediately

Step 2: Identify Authentication Type

Different APIs use different auth methods. Test accordingly:

Basic Authentication (username:password)

bash
source .env && curl -s --compressed \
  -u "${SERVICE_USERNAME}:${SERVICE_PASSWORD}" \
  "https://api.example.com/endpoint"

Or with explicit header:

bash
source .env && echo -n "${SERVICE_USERNAME}:${SERVICE_PASSWORD}" | base64
# Then use the output:
curl -s --compressed \
  -H "Authorization: Basic <base64_string>" \
  "https://api.example.com/endpoint"

Bearer Token

bash
source .env && curl -s --compressed \
  -H "Authorization: Bearer ${SERVICE_API_KEY}" \
  "https://api.example.com/endpoint"

API Key in Header

bash
source .env && curl -s --compressed \
  -H "X-API-Key: ${SERVICE_API_KEY}" \
  "https://api.example.com/endpoint"

API Key in Query Parameter

bash
source .env && curl -s --compressed \
  "https://api.example.com/endpoint?api_key=${SERVICE_API_KEY}"

Step 3: Check Environment Variable Names

A common issue is mismatched environment variable names between:

  • What's in your .env file
  • What the plugin code expects
  • What the OpenAPI generator configured

Debug command:

bash
# Show variable names only (not values)
grep -o '^[A-Z_]*=' .env

Common naming mismatches:

.env hasCode expectsFix
SERVICE_API_USERNAMESERVICE_API_KEYAlign naming
SERVICE_TOKENSERVICE_API_TOKENCheck package.json authEnvVar
API_KEYSERVICE_API_KEYAdd service prefix

Fix in package.json:

json
{
  "openapiSpec": {
    "authEnvVar": "SERVICE_API_USERNAME",
    "authSecretEnvVar": "SERVICE_API_PASSWORD"
  }
}

Then regenerate: pets generate-openapi

Step 4: Handle Response Compression

Many APIs return compressed responses. If you see garbled output like:

�      �VJ�OIU�R��/q,-��/ʬJMq-*�/R�Q*�,�I��%

Add --compressed to curl:

bash
curl -s --compressed "https://api.example.com/endpoint"

Step 5: Interpret Error Responses

StatusMeaningDebug Action
401 UnauthorizedBad credentials or wrong auth methodVerify credentials, check auth type
403 ForbiddenValid auth but no permissionCheck API scopes/permissions
400 Bad RequestInvalid parametersRead error message, check API docs
404 Not FoundWrong endpoint or resourceVerify URL path
429 Too Many RequestsRate limitedAdd delays between requests

Example 401 debugging:

bash
# If this fails with 401:
curl -s -u "user:pass" "https://api.example.com/me"

# Try these:
# 1. Check if credentials are expired/regenerated
# 2. Verify auth method (maybe it's Bearer, not Basic)
# 3. Check if API access is enabled in dashboard
# 4. Look for IP restrictions

Step 6: Verify Plugin Configuration

After curl works, ensure plugin matches:

Check openapi-client.ts auth setup:

typescript
// Should match your working curl command
const apiKey = process.env.SERVICE_API_USERNAME  // Match .env name
const apiSecret = process.env.SERVICE_API_PASSWORD

// Basic auth encoding
const credentials = `${apiKey}:${apiSecret}`
const encoded = Buffer.from(credentials).toString("base64")
headers["Authorization"] = `Basic ${encoded}`

Check index.ts loads correct vars:

typescript
const env = loadEnv("service")
const API_USERNAME = env.SERVICE_API_USERNAME  // Must match .env
const API_PASSWORD = env.SERVICE_API_PASSWORD

Common Issues & Solutions

Issue: curl works but plugin returns 401

This is the most common issue and is caused by environment variables not being loaded before the openapi-client module initializes.

Root cause: The generated openapi-client.ts reads process.env.MY_API_KEY at module load time. JavaScript modules execute top-level code when first imported. If .env isn't loaded yet, auth headers are built with undefined.

Solution: The OpenAPI generator now uses lazy authentication - regenerate your client:

bash
cd pets/my-api
pets generate-openapi

The new generator creates a getAuthHeaders() function that reads env vars at request time:

typescript
// NEW: Lazy auth - reads env vars fresh on each request
function getAuthHeaders(): Record<string, string> {
  const headers = { "Content-Type": "application/json" }
  const apiKey = process.env.MY_API_KEY  // Read at request time!
  if (apiKey) {
    headers["Authorization"] = `Bearer ${apiKey}`
  }
  return headers
}

async function fetchAPI(url: string, options: RequestInit) {
  const authHeaders = getAuthHeaders()  // Fresh headers per request
  // ...
}

Verification:

bash
# After regenerating, test the plugin
bun test:pet my-api --query "test connection"

Issue: "401 Unauthorized" but credentials look correct

Causes:

  1. Wrong environment variable name in code
  2. Credentials expired or regenerated
  3. Wrong authentication method
  4. API access not enabled in provider dashboard

Debug:

bash
# 1. Verify env var names match
grep -o '^[A-Z_]*=' .env
grep -r "process.env\." index.ts openapi-client.ts

# 2. Test credentials directly
source .env && curl -s -u "${VAR_NAME}:${VAR_PASS}" "https://api.example.com/test"

# 3. Try different auth methods
source .env && curl -s -H "Authorization: Bearer ${VAR_NAME}" "https://api.example.com/test"

Issue: Plugin works with curl but not in the OpenPets harness

Causes:

  1. Plugin not reading .env file
  2. Environment variables not exported
  3. Wrong variable prefix for loadEnv()

Debug:

bash
# Check if plugin sees the env vars
bun test:pet service --query "test connection" 2>&1 | grep -i "not configured"

# Verify .env is in the right location (pet directory or project root)
ls -la .env

# Check loadEnv prefix matches variable names
# loadEnv("myservice") expects MYSERVICE_* variables

Issue: "Cannot parse JSON" or garbled response

Cause: Response is compressed (gzip/deflate)

Fix in generated client:

typescript
// The fetch API handles this automatically in most cases
// But ensure Accept-Encoding is set if needed
headers["Accept-Encoding"] = "gzip, deflate"

Issue: Works locally but fails in CI/production

Causes:

  1. Environment variables not set in CI
  2. IP restrictions on API
  3. Different Node.js/Bun version

Debug:

bash
# Verify env vars are set (without revealing values)
env | grep SERVICE_ | cut -d= -f1

Regenerating After Fixes

After fixing configuration issues, always regenerate the OpenAPI client:

bash
cd pets/your-service
pets generate-openapi --verbose

This ensures:

  • Auth configuration from package.json is applied
  • Environment variable names are consistent
  • Headers and auth methods match your settings

Template: Debugging New Pet Connection

When a new pet fails to connect, run through this checklist:

bash
# 1. Navigate to pet directory
cd pets/your-service

# 2. Check what env vars exist
grep -o '^[A-Z_]*=' .env

# 3. Check what code expects
grep "process.env\." openapi-client.ts | head -5

# 4. Test with curl (substitute your auth method)
source .env && curl -s --compressed \
  -u "${YOUR_API_USER}:${YOUR_API_PASS}" \
  -H "Accept: application/json" \
  "https://api.yourservice.com/test-endpoint"

# 5. If curl works but plugin doesn't, check package.json
cat package.json | jq '.openapiSpec'

# 6. Fix authEnvVar/authSecretEnvVar names and regenerate
pets generate-openapi --verbose

# 7. Test again with the OpenPets harness
bun test:pet your-service --query "test connection"

Summary

The most common authentication debugging issues are:

  1. Environment variable name mismatch - The #1 cause of "works in curl, fails in plugin"
  2. Wrong auth method - Basic vs Bearer vs API Key header
  3. Compressed responses - Use --compressed with curl
  4. Credentials not loaded - Check .env location and loadEnv() prefix

Always start debugging by testing with curl directly using source .env to load credentials. This isolates whether the problem is with the credentials themselves or with the plugin code.