ssl_error

ssl_error means the browser reached the target server, but refused the HTTPS connection because of the site's certificate or TLS setup. The certificate can be expired, issued for another host name, self-signed, or signed by an unknown authority. The problem is on the target site. Your request is correct.

Field Value
HTTP status 422 Unprocessable Entity
error.type target_error
error.code ssl_error. Some failures arrive today with the generic code screenshot_failed and the same browser message, so match on both
retryable true (but a retry gives the same result until the site changes)
Credit used No

See the target errors in the error code reference.

Example response

{
  "error": {
    "type": "target_error",
    "code": "ssl_error",
    "message": "net::ERR_CERT_DATE_INVALID at https://expired.badssl.com/",
    "retryable": true
  },
  "request_id": "req_61b0e4f9a2c73d58"
}

The browser's error name in the message, such as ERR_CERT_DATE_INVALID, tells you which check failed.

What the error names mean

We opened the public test hosts of badssl.com in headless Chrome 148 and recorded each result:

Test host Chrome error What is wrong Fix on the site
expired.badssl.com ERR_CERT_DATE_INVALID Certificate expired, or not valid yet Renew it; check that auto-renewal runs
wrong.host.badssl.com ERR_CERT_COMMON_NAME_INVALID Certificate does not cover this host name Add the name, often www. or the bare domain, to the certificate
self-signed.badssl.com ERR_CERT_AUTHORITY_INVALID Self-signed certificate Use a certificate from a public authority
untrusted-root.badssl.com ERR_CERT_AUTHORITY_INVALID Root not trusted by Chrome Same as above
tls-v1-0.badssl.com:1010 ERR_SSL_VERSION_OR_CIPHER_MISMATCH Server offers only old TLS versions Enable TLS 1.2 and 1.3
incomplete-chain.badssl.com loaded Missing intermediate certificate Serve the full chain anyway
revoked.badssl.com loaded Revoked certificate Replace it anyway

The last two rows show that Chrome is lenient in some cases: it fetched the missing intermediate certificate itself, and it did not reject the revoked certificate. Other clients behave differently, and Chrome's behaviour can change, so fix those sites too.

Check the certificate yourself

Run curl from a server outside your company network. A laptop can trust extra root certificates, for example from a corporate proxy, that the renderer does not have.

# Print the certificate's subject, host names, issuer and expiry date
curl -sv -o /dev/null https://example.com/ 2>&1 \
  | grep -E 'subject:|subjectAltName|issuer:|expire date'

For a healthy site, subjectAltName reports that the host matched and expire date is in the future. For a broken site, curl stops with a reason. In our test, it printed certificate has expired for the expired host, self signed certificate for the self-signed host, and no alternative certificate subject name matches target host name for the wrong host.

Ways forward when you do not own the site

The API has no option to skip certificate checks. A screenshot of a page with a broken certificate is not a page that your users can open either. You have three choices:

  1. Wait and record it. Expired certificates are often renewed within hours. Retry later, for example in the next scheduled run, not in a tight loop.
  2. Use http:// if the site serves its public pages over plain HTTP without a redirect to HTTPS. Never do this for pages that need cookies, headers or other credentials.
  3. Send the markup. If you can get the HTML some other way, render it with the html input. See content input.

Handle it in code

Do not put ssl_error in a fast retry loop. With the Ruby SDK, record the browser's reason for the site owner and move on:

require 'renderscreenshot'

client = RenderScreenshot.client(ENV.fetch('RS_API_KEY'))
url = ARGV.fetch(0)

begin
  File.binwrite('page.png', client.take(RenderScreenshot::TakeOptions.url(url)))
  puts "saved page.png for #{url}"
rescue RenderScreenshot::Error => e
  raise unless e.code == 'ssl_error' || e.message.match?(/ERR_(CERT|SSL)_/)

  # The certificate is the site's problem, so a retry gives the same result.
  # Record it and show the owner the browser's reason, e.g. ERR_CERT_DATE_INVALID.
  reason = e.message[/ERR_[A-Z_]+/] || e.message
  warn "TLS problem on #{url}: #{reason} (request #{e.request_id})"
  exit 1
end

Tested on 2026-10-15. The Chrome table came from Puppeteer 24.43.1 with Chrome 148 on Node 22.18. The curl results came from curl 8.7.1 on macOS. The Ruby script ran with Ruby 4.0.7 and the renderscreenshot gem 1.0.0 against a local mock that returns the API's error envelope.

Related in this topic

Was this page helpful?