Errors
Every failure returned by the SDK is a *polipage.Error. Use errors.As to extract typed fields and errors.Is against the package-level sentinels (ErrUnauthorized, ErrDocumentGone, …) for quick branching.
A sentinel matches by code, not by HTTP status. ErrUnauthorized matches MISSING_API_KEY and INVALID_API_KEY, and ErrRateLimit matches QUOTA_EXCEEDED and OVERAGE_CAP_EXCEEDED. To catch every 429, use the IsRateLimitError() method; for a failed render, compare Code with ErrCodeProjectNotFound, ErrCodeProjectNotInEnvironment or ErrCodeTemplateNotFound.
The error type
Section titled “The error type”var perr *polipage.Errorif errors.As(err, &perr) { fmt.Println(perr.Code, perr.StatusCode, perr.RequestID, perr.Message)}*Error implements Unwrap, so errors.Unwrap(err) gives you the underlying I/O / context cause when present.
SDK-internal codes
Section titled “SDK-internal codes”These originate inside the SDK before any HTTP call (or in response to a network-layer failure). They are lowercase.
| Code | When | Recovery |
|---|---|---|
invalid_options | The options passed to NewClient (or a per-call WithX option) are invalid. A missing or empty API key gives the message `apiKey is required`. | Fix the call site. Surfaced on the first method call. |
network_error | TCP/TLS-layer failure reaching the API. The message is `Network request failed`; the net/http error is the `Cause`. | Automatically retried up to WithMaxRetries times. |
timeout | The request did not complete within the configured WithTimeout. The message is `Request timed out after <ms>ms`. | Automatically retried up to WithMaxRetries times. Raise WithTimeout if it keeps tripping. |
aborted | The caller-supplied context was cancelled, during a request, a retry wait or the PDF download. The message is `Request was aborted`. | Not retryable. Surface to the caller. |
io_failed | A local filesystem write failed inside RenderToFile. | Fix the path / permissions. Unwrap to inspect the os error. |
DOWNLOAD_FAILED | Fetching a presigned PDF URL failed: `Failed to download PDF: HTTP <status>` with the storage `StatusCode` (an expired URL gets 403), or `Failed to download PDF: network error` without one, also when the connection drops while the body is read. | Never retryable, whatever the storage status. Call Documents.Get to refresh the presigned URL and retry. |
API codes
Section titled “API codes”Returned by the Poli Page API. Uppercase, snake-case. The SDK forwards the code verbatim.
| Code | When | Recovery |
|---|---|---|
INVALID_API_KEY | The API key is malformed or revoked. | Rotate the key in the dashboard. |
MISSING_API_KEY | No API key was provided in the request. | Check WithAPIKey was supplied to NewClient. |
PROJECT_NOT_FOUND | No project matches the slug. | Check the slug. If getting-started was renamed or deleted, use a fresh organization. |
PROJECT_NOT_IN_ENVIRONMENT | The project is not published in the key's environment. | Publish the project to that environment, or use a key of the environment it is published in. |
TEMPLATE_NOT_FOUND | The published project has no template with that slug. | Check the template slug. |
DOCUMENT_NOT_FOUND | No stored document matches the documentId. | Check the id. |
DOCUMENT_GONE | The document was deleted or has expired (sandbox documents expire after 30 days). | Render it again. |
VALIDATION_ERROR | The request failed validation. | Read Message for the failing field. |
MISSING_DATA | The request body lacks the required Data field. | Pass a non-nil Data on every render call (an empty map is fine). |
QUOTA_EXCEEDED | The organization's monthly quota is exhausted (429). | Not retried: it lasts until the monthly reset. Upgrade the plan for more renders. |
OVERAGE_CAP_EXCEEDED | The organization reached its overage cap (429). | Not retried: it lasts until the monthly reset. Raise the cap in the dashboard. |
STORAGE_ACCESS_DENIED | The organization's own storage bucket refuses access (502). | Not retried: restore the bucket role, then call again. |
RATE_LIMIT_EXCEEDED | Too many requests (429). | Retried like any 429, honoring Retry-After. |
ORGANIZATION_PAYMENT_GRACE | The organization's billing is past due. | Update the payment method in the dashboard. |
PUBLISH_IN_PROGRESS | A publish holds the project in the key's environment (503). | Retried like any 5xx, honoring Retry-After. |
INTERNAL_ERROR | The API returned 5xx. Also the code of an error answer whose body is not JSON (a gateway page) or fails while it is read, with its real status and the message `HTTP <status>`. | Automatically retried; contact support if it persists. |
The full list of API codes lives in the reference.
Example
Section titled “Example”package main
import ( "context" "errors" "fmt" "os"
"github.com/poli-page/sdk-go" "github.com/poli-page/sdk-go/option")
func main() { client := polipage.NewClient(option.WithAPIKey(os.Getenv("POLI_PAGE_API_KEY")))
_, err := client.Render.PDF(context.Background(), polipage.RenderInput{ Project: "billing", Template: "invoice", Data: map[string]any{"invoiceNumber": "INV-001"}, }) if err != nil { var perr *polipage.Error if errors.As(err, &perr) { if perr.IsRateLimitError() { // RATE_LIMIT_EXCEEDED after the SDK's built-in retries, or // QUOTA_EXCEEDED / OVERAGE_CAP_EXCEEDED, which the SDK never // retries: queue the job for later, or report it as here. fmt.Fprintln(os.Stderr, "rate limited, try again later; request id:", perr.RequestID) os.Exit(1) } fmt.Println("code:", perr.Code, "request id:", perr.RequestID) } panic(err) }}