Skip to main content

Error Handling & Troubleshooting

This guide helps you diagnose and resolve common issues with VanityCert.

API Error Responses

All API errors follow this format:

{
"error": {
"code": "dns_validation_failed",
"message": "DNS validation failed: CNAME record not found",
"details": {
"domain": "app.yourdomain.com",
"expected_cname": "my.vanitycert.com"
}
}
}

Error Structure:

FieldTypeDescription
codestringMachine-readable error code
messagestringHuman-readable error message
detailsobjectAdditional context (optional)

HTTP Status Codes

Status CodeMeaningCommon Causes
200 OKRequest successful-
201 CreatedResource created-
400 Bad RequestInvalid request dataMissing fields, validation errors
401 UnauthorizedInvalid/missing API keyWrong API key, expired key
403 ForbiddenInsufficient permissionsAPI key lacks required permissions
404 Not FoundResource doesn't existWrong ID, resource deleted
422 Unprocessable EntityValidation failedInvalid domain format, duplicate
429 Too Many RequestsRate limit exceededToo many API requests
500 Internal Server ErrorServer errorContact support
503 Service UnavailableService temporarily downCheck status page

Common Errors

DNS Validation Errors

CNAME Record Not Found

Error:

{
"error": {
"code": "cname_not_found",
"message": "DNS validation failed: CNAME record not found",
"details": {
"domain": "app.yourdomain.com",
"expected_target": "my.vanitycert.com"
}
}
}

Causes:

  • CNAME record not configured
  • DNS not yet propagated
  • Wrong subdomain configured

Resolution:

  1. Verify CNAME exists:

    dig app.yourdomain.com CNAME

    Expected output:

    app.yourdomain.com. 300 IN CNAME my.vanitycert.com.
  2. Check CNAME target: Must be exactly my.vanitycert.com (case-insensitive)

  3. Wait for DNS propagation: Can take 5-30 minutes, up to 24 hours maximum

  4. Check from multiple DNS servers:

    dig @8.8.8.8 app.yourdomain.com CNAME     # Google
    dig @1.1.1.1 app.yourdomain.com CNAME # Cloudflare
    dig @208.67.222.222 app.yourdomain.com CNAME # OpenDNS
  5. Remove conflicting records: Delete any A or AAAA records for the same subdomain


Incorrect CNAME Target

Error:

{
"error": {
"code": "invalid_cname_target",
"message": "CNAME points to wrong target",
"details": {
"domain": "app.yourdomain.com",
"actual_target": "www.vanitycert.com",
"expected_target": "my.vanitycert.com"
}
}
}

Resolution:

Update CNAME record to point to my.vanitycert.com:

# Correct
app.yourdomain.com. IN CNAME my.vanitycert.com.

# Incorrect
app.yourdomain.com. IN CNAME www.vanitycert.com.
app.yourdomain.com. IN CNAME vanitycert.com.

DNS Validation Timeout

Error:

{
"error": {
"code": "dns_validation_timeout",
"message": "DNS validation failed after 24 hours",
"details": {
"domain": "app.yourdomain.com",
"started_at": "2025-01-01T12:00:00Z",
"timed_out_at": "2025-01-02T12:00:00Z"
}
}
}

Causes:

  • DNS never configured
  • CNAME incorrect for entire 24 hour period
  • DNS propagation issues

Resolution:

  1. Fix DNS configuration
  2. Delete the failed domain
  3. Re-create domain (validation will retry)

Certificate Issuance Errors

Certificate Request Failed

Error:

{
"error": {
"code": "certificate_request_failed",
"message": "Failed to request certificate from ACME provider",
"details": {
"domain": "app.yourdomain.com",
"acme_error": "Rate limit exceeded"
}
}
}

Causes:

  • ACME provider rate limits
  • Server unreachable
  • Invalid domain format
  • Network connectivity issues

Resolution:

  1. Wait and retry (if rate limited)
  2. Check server health:
    curl -I https://app.yourdomain.com
  3. Contact support with domain ID and error details

Certificate Issuance Timeout

Error:

{
"error": {
"code": "certificate_timeout",
"message": "Certificate issuance timed out after 24 hours",
"details": {
"domain": "app.yourdomain.com",
"certificate_request_id": "abc123"
}
}
}

Causes:

  • ACME provider processing delay (rare)
  • Backend server issues
  • Network connectivity problems

Resolution:

  1. Contact support with domain ID
  2. Provide certificate request ID from error
  3. May require manual intervention

API Errors

Invalid API Key

Error:

{
"error": {
"code": "invalid_api_key",
"message": "Invalid or expired API key"
}
}

HTTP Status: 401 Unauthorized

Causes:

  • API key not included in request
  • Wrong API key
  • API key revoked
  • API key expired

Resolution:

  1. Verify API key in request:

    curl -X GET https://app.vanitycert.com/api/domains \
    -H "X-API-KEY-ID: vc_pk_YOUR_KEY_ID_HERE" \
    -H "X-API-KEY: YOUR_SECRET_KEY_HERE"
  2. Check API key format:

    • Production: vc_live_...
    • Test: vc_test_... (if available)
  3. Generate new API key:

    • Dashboard → Developer → API Keys → Create New

Insufficient Permissions

Error:

{
"error": {
"code": "insufficient_permissions",
"message": "API key lacks required permissions",
"details": {
"required_permission": "domains.write",
"api_key_permissions": ["domains.read"]
}
}
}

HTTP Status: 403 Forbidden

Resolution:

  1. Check API key permissions: Dashboard → Developer → API Keys

  2. Update permissions or create new key with required permissions:

    • domains.read - List/view domains
    • domains.write - Create/update/delete domains
    • webhooks.read - List webhooks
    • webhooks.write - Manage webhooks

Rate Limit Exceeded

Error:

{
"error": {
"code": "rate_limit_exceeded",
"message": "Too many requests. Please try again later.",
"details": {
"limit": 1000,
"remaining": 0,
"reset_at": "2025-01-01T13:00:00Z"
}
}
}

HTTP Status: 429 Too Many Requests

Headers:

X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1704118800

Resolution:

  1. Wait until reset time:

    const resetTime = parseInt(response.headers['x-ratelimit-reset']);
    const waitSeconds = resetTime - Math.floor(Date.now() / 1000);
    await sleep(waitSeconds * 1000);
  2. Implement exponential backoff:

    async function retryWithBackoff(fn, maxRetries = 3) {
    for (let i = 0; i < maxRetries; i++) {
    try {
    return await fn();
    } catch (error) {
    if (error.status === 429 && i < maxRetries - 1) {
    const delay = Math.pow(2, i) * 1000; // 1s, 2s, 4s
    await sleep(delay);
    continue;
    }
    throw error;
    }
    }
    }
  3. Optimize API usage:

    • Cache responses
    • Use bulk endpoints where possible
    • Batch operations

Resource Not Found

Error:

{
"error": {
"code": "resource_not_found",
"message": "Domain not found",
"details": {
"domain_id": 999
}
}
}

HTTP Status: 404 Not Found

Causes:

  • Wrong domain ID
  • Domain deleted
  • Domain belongs to different organization

Resolution:

  1. Verify domain ID:
    curl -X GET https://app.vanitycert.com/api/domains \
    -H "X-API-KEY-ID: vc_pk_abc123def456" \

-H "X-API-KEY: sk_1234567890abcdef1234567890abcdef"


2. **Check domain exists in dashboard**

3. **Ensure using correct organization's API key**

---

#### Validation Error

**Error:**
```json
{
"error": {
"code": "validation_error",
"message": "Invalid request data",
"details": {
"errors": {
"url": ["The url field is required"],
"server_id": ["The server_id must be an integer"]
}
}
}
}

HTTP Status: 422 Unprocessable Entity

Resolution:

  1. Check required fields:

    {
    "url": "app.yourdomain.com",
    "server_id": 123
    }
  2. Validate data types:

    • url: string
    • server_id: integer
    • send_notifications: boolean
  3. Check format requirements:

    • URLs: No http:// or https://
    • Subdomains only (not apex domains)

Webhook Errors

Invalid Webhook Signature

Error in your logs:

Webhook signature verification failed
Expected: sha256=abc123...
Received: sha256=def456...

Causes:

  • Using wrong webhook secret
  • Not reading raw request body
  • Modifying body before verification

Resolution:

  1. Verify secret matches webhook configuration:

    const secret = process.env.WEBHOOK_SECRET; // Must match
  2. Read raw body:

    // ✅ Correct
    app.post('/webhooks',
    express.raw({type: 'application/json'}),
    handler
    );

    // ❌ Wrong
    app.post('/webhooks',
    express.json(), // Parses body first!
    handler
    );
  3. Don't modify body:

    // Verify BEFORE parsing
    const signature = req.headers['x-vanitycert-signature'];
    const isValid = verifySignature(req.body, signature, secret);

    // THEN parse
    const event = JSON.parse(req.body);

Webhook Delivery Failures

Symptoms:

  • Webhooks marked as "failed" in dashboard
  • Not receiving webhook events

Causes:

  • Endpoint not responding
  • Endpoint returning non-2xx status
  • Request timeout (>30 seconds)
  • Network/firewall issues

Resolution:

  1. Test endpoint manually:

    curl -X POST https://api.yourdomain.com/webhooks/vanitycert \
    -H "Content-Type: application/json" \
    -d '{"event":"test","domain_id":123}'
  2. Check endpoint responds quickly (< 5s):

    app.post('/webhooks/vanitycert', async (req, res) => {
    // Respond immediately
    res.status(200).send('OK');

    // Process async
    processWebhook(req.body).catch(console.error);
    });
  3. Return correct status codes:

    • 200-299: Success
    • 400-499: Don't retry
    • 500-599: Retry
  4. Check firewall rules allow VanityCert's IPs

  5. Use HTTPS endpoints (not HTTP)


Debugging Tips

Enable Detailed Logging

const axios = require('axios');

// Log all API requests/responses
axios.interceptors.request.use(request => {
console.log('API Request:', {
method: request.method,
url: request.url,
headers: request.headers,
data: request.data
});
return request;
});

axios.interceptors.response.use(
response => {
console.log('API Response:', {
status: response.status,
data: response.data
});
return response;
},
error => {
console.error('API Error:', {
status: error.response?.status,
data: error.response?.data,
message: error.message
});
throw error;
}
);

Check Domain Status

# Get full domain details
curl -X GET https://app.vanitycert.com/api/domains/456 \
-H "X-API-KEY-ID: vc_pk_abc123def456" \
-H "X-API-KEY: sk_1234567890abcdef1234567890abcdef" \
| jq '.'

Review Certificate Audit Log

Dashboard → Reports → Certificate Audit

Shows complete history of all certificate events with error details.

Test DNS Configuration

# Check CNAME record
dig app.yourdomain.com CNAME +short

# Check from specific DNS server
dig @8.8.8.8 app.yourdomain.com CNAME +short

# Check with trace (shows full resolution path)
dig app.yourdomain.com CNAME +trace

Verify SSL Certificate

# Check if SSL is working
curl -vI https://app.yourdomain.com 2>&1 | grep -i ssl

# Get certificate details
openssl s_client -connect app.yourdomain.com:443 -servername app.yourdomain.com < /dev/null 2>/dev/null | openssl x509 -text -noout

Getting Help

Before Contacting Support

Gather this information:

  1. Domain ID or Domain URL
  2. Error message (full JSON response)
  3. Steps to reproduce the issue
  4. DNS configuration (dig output)
  5. Timestamp when issue occurred
  6. API request/response logs

Support Channels

Email: support@vanitycert.com

Dashboard: https://app.vanitycert.com/support

Status Page: https://status.vanitycert.com

Check status page first for known issues.

Emergency Support

For urgent issues affecting production:

  1. Email: support@vanitycert.com with subject "URGENT"
  2. Include impact description
  3. Provide all debugging information above

Response Times:

  • Normal: Within 24 hours
  • Urgent: Within 4 hours
  • Critical (service down): Within 1 hour

Best Practices

Error Handling in Code

async function createDomain(url, serverId) {
try {
const response = await axios.post(
'https://app.vanitycert.com/api/domains',
{ url, server_id: serverId },
{
headers: {
'X-API-KEY-ID': process.env.VANITYCERT_API_KEY_ID,
'X-API-KEY': process.env.VANITYCERT_API_KEY,
'Content-Type': 'application/json'
}
}
);

return response.data;

} catch (error) {
if (error.response) {
// API returned error response
const { code, message, details } = error.response.data.error;

switch (code) {
case 'cname_not_found':
console.error('DNS not configured:', details.domain);
// Handle DNS issue
break;

case 'rate_limit_exceeded':
console.error('Rate limited. Retry after:', details.reset_at);
// Implement backoff
break;

case 'invalid_api_key':
console.error('Invalid API key');
// Check API key configuration
break;

default:
console.error('API Error:', code, message);
}

throw new Error(`Failed to create domain: ${message}`);

} else if (error.request) {
// Request made but no response
console.error('No response from API');
throw new Error('API unreachable');

} else {
// Request setup error
console.error('Request error:', error.message);
throw error;
}
}
}

Retry Logic

async function withRetry(fn, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await fn();

} catch (error) {
const isLastAttempt = attempt === maxRetries;
const isRetryable = error.response?.status >= 500 ||
error.response?.status === 429;

if (!isRetryable || isLastAttempt) {
throw error;
}

const delay = Math.min(1000 * Math.pow(2, attempt), 10000);
console.log(`Retry attempt ${attempt}/${maxRetries} after ${delay}ms`);
await sleep(delay);
}
}
}

// Usage
await withRetry(() => createDomain('app.example.com', 123));

Monitoring & Alerts

// Monitor domain health
async function checkDomainHealth() {
const domains = await api.getDomains();

const issues = domains.filter(d =>
d.dns_status === 'error' ||
d.ssl_status === 'error' ||
(d.renews_on && daysUntil(d.renews_on) < 7)
);

if (issues.length > 0) {
await sendAlert({
title: 'Domain Health Issues',
message: `${issues.length} domain(s) need attention`,
domains: issues.map(d => d.url)
});
}
}

// Run daily
setInterval(checkDomainHealth, 24 * 60 * 60 * 1000);

See Also