{"schema_version":"1.0","publisher":"Primitive SDKs","canonical_url":"https://test.abhinandan.one/x402-payments-overview-04a296ff/go-x402-errors","markdown_url":"https://test.abhinandan.one/x402-payments-overview-04a296ff/go-x402-errors.md","article":{"id":"ab3f2403-8a38-488a-b9d8-18bbcf940dad","article_slug":"go-x402-errors","parent_article_slug":"x402-payments-overview-04a296ff","parent_article_title":"x402 Payments Overview","kind":"reference","published_at":"2026-08-11T18:55:00.393517+00:00","keywords":["X402Error","errors.As","Status 0 indeterminate payment","RetryAfter x402","sdk-go x402 errors","Pay() error handling"],"meta_description":"A Status of 0 on *primitive.X402Error means the request may never have reached the server, so a Pay() failure at that status is an indeterminate outcome.","og_image_url":null,"source_file_paths":["sdk-go/README.md","sdk-go/x402.go"],"recording_id":null,"replayable":false,"task_name":"x402 Errors","category":"Go SDK","summary":null,"description":"Reference for the Go SDK's *primitive.X402Error type: how to inspect its Status, Body, and RetryAfter fields, and how to handle a Status 0 error where the payment outcome is indeterminate.","content_kind":"repo_page","content_markdown":"## What error type x402 methods return\n\nEvery method on `primitive.X402Client` returns a `*primitive.X402Error` on a client-side, transport, or non-2xx server error. Use `errors.As` to pull it out of the returned `error`:\n\n```go\nimport (\n\t\"errors\"\n\t\"fmt\"\n\n\tprimitive \"github.com/primitivedotdev/sdks/sdk-go\"\n)\n\nreceipt, err := client.Pay(ctx, challenge, payer)\nif err != nil {\n\tvar x402Err *primitive.X402Error\n\tif errors.As(err, &x402Err) {\n\t\tfmt.Println(x402Err.Status, x402Err.Message)\n\t}\n\treturn err\n}\n```\n\nThis covers `Charge`, `Pay`, `RegisterPayoutAddress`, `GetChallenge`, `ListPayoutAddresses`, `SetSpendPolicy`, `CreateEmailChallenge`, and the other `X402Client` methods described in [x402 Payments Overview](x402-payments-overview), [Registering a Payout Address](go-x402-register-payout), [Creating a Payment Challenge](go-x402-create-charge), and [x402 Spend Policy](go-x402-spend-policy).\n\n## X402Error fields\n\n| Field | Type | Description |\n| --- | --- | --- |\n| `Status` | `int` | The HTTP status code, or `0` for a client-side/transport error that never reached the server. |\n| `Message` | `string` | Human-readable error message (from `Error()`). |\n| `Body` | parsed error envelope or `nil` | The parsed error response body, when the server returned one. |\n| `RetryAfter` | pointer or `nil` | The `Retry-After` response header value, when the server sent one (e.g. on a 429). |\n\n`Error()` returns `Message`, so `*primitive.X402Error` satisfies the standard `error` interface directly.\n\n## Status 0: the request may never have been sent\n\nA `Status` of `0` means the error is client-side or transport-level: DNS failure, connection refused, TLS error, a client-side timeout, or any other failure before a response was received.\n\n<Warning>\n\nOn `Pay`, a `Status == 0` error means the request may not have been sent, so the payment outcome is **indeterminate**. Do not assume the payment failed and retry blindly, since the original request may have reached the server and be settling.\n\n</Warning>\n\nRecovery for a `Status == 0` error on `Pay`:\n\n1. Call `client.GetChallenge(ctx, id)` to re-hydrate the challenge and check its state before retrying `Pay`.\n2. If the challenge shows as already paid or settled, do not resubmit.\n3. If the challenge is still open, retry `Pay` with the re-hydrated challenge. `GetChallenge` exists for exactly this: re-hydrating a challenge by id to retry `Pay`, for example after a restart.\n\n## Retry-After\n\nWhen the server returns a `429` (or any response carrying a `Retry-After` header), that value is available on `RetryAfter`:\n\n```go\nvar x402Err *primitive.X402Error\nif errors.As(err, &x402Err) && x402Err.RetryAfter != nil {\n\ttime.Sleep(time.Duration(*x402Err.RetryAfter) * time.Second)\n\t// retry\n}\n```\n\n## Non-2xx server errors\n\nFor any non-2xx response the server did return, `Body` carries the parsed error envelope (when the response body was JSON) so you can inspect the server's error code/message beyond `Message`.\n\n```go\nreceipt, err := client.Pay(ctx, challenge, payer)\nif err != nil {\n\tvar x402Err *primitive.X402Error\n\tif errors.As(err, &x402Err) {\n\t\tswitch {\n\t\tcase x402Err.Status == 0:\n\t\t\t// transport-level; outcome indeterminate, see above\n\t\tcase x402Err.Status == 429:\n\t\t\t// rate limited; consult x402Err.RetryAfter\n\t\tcase x402Err.Status >= 400 && x402Err.Status < 500:\n\t\t\t// rejected by the server (e.g. payment_declined, validation error)\n\t\tcase x402Err.Status >= 500:\n\t\t\t// server error; safe to retry\n\t\t}\n\t}\n}\n```\n\n## Signing and validation errors\n\nMethods that validate input locally before making a network call (for example, a malformed challenge passed to `Pay`, or an invalid signer) also return `*primitive.X402Error` with `Status: 0`, so the same `errors.As` pattern catches both \"never reached the server\" transport failures and \"rejected before any request was made\" validation failures. Check `Message` to distinguish them.\n\n## Next steps\n\n<CardGroup cols={2}>\n\n<Card title=\"Creating a Payment Challenge\" href=\"go-x402-create-charge\">\n\nCreate an x402 challenge as the payee with Client.Charge.\n\n</Card>\n\n<Card title=\"x402 Signing Primitives\" href=\"go-x402-signing-primitives\">\n\nDrive nonce derivation and signing yourself when Pay doesn't fit your flow.\n\n</Card>\n\n<Card title=\"x402 Payments Overview\" href=\"x402-payments-overview\">\n\nUnderstand the non-custodial x402 payment model shared across SDKs.\n\n</Card>\n\n<Card title=\"Error Handling\" href=\"go-error-handling\">\n\nInspect the Go SDK's other error types: APIError, PrimitiveWebhookError, and more.\n\n</Card>\n\n</CardGroup>","canonical_base_url":"https://test.abhinandan.one","seo_indexing_enabled":true,"last_modified":"2026-08-21T18:22:43.359885+00:00","video_url":null,"voiceover_url":null,"tools_used":[],"demonstrated_by":[],"steps":[],"related_links":[],"intro":null,"prerequisites":[],"verification":[],"troubleshooting":[],"suggest_edit_url":"https://github.com/abhi-browzer/primitive-sdks/edit/main/sdk-go/README.md","raise_issue_url":"https://github.com/abhi-browzer/primitive-sdks/issues/new?title=Docs+feedback%3A+x402+Errors&body=Page%3A+https%3A%2F%2Ftest.abhinandan.one%2Fgo-x402-errors","page_feedback_enabled":true,"verified_ref":null,"verified_at":"2026-08-11T18:38:45.205849+00:00"}}