Skip to main content
Version: v2.0

Handling errors

Every SDK operation returns an error as its last value. Errors fall into two groups: API errors returned by ServiceNow (wrong credentials, missing record, throttling) and usage errors raised by the SDK before a request is sent (nil configuration, missing adapter). For why the split exists, see Core Concepts; this page covers how to handle each kind.

API errors

When ServiceNow responds with a failure status code, the SDK deserializes the response body into a typed error that wraps the platform's error.message, error.detail, and status fields. Match the type with errors.As:

response, err := client.Now().Table("{TableName}").ByID("{SysID}").Get(ctx, nil)
if err != nil {
var notFound *core.NotFoundError
var unauthorized *core.UnauthorizedError

switch {
case errors.As(err, &notFound):
// The record does not exist — treat as absent, not fatal.
case errors.As(err, &unauthorized):
// Credentials rejected — re-authenticate or fail fast.
default:
return err
}
}

The mapped types, one per status class:

TypeStatus
core.BadRequestError400
core.UnauthorizedError401
core.ForbiddenError403
core.NotFoundError404
core.TooManyRequestsError429
core.ServerError5XX
core.ServiceNowErrorany other error status

All of them embed core.ServiceNowError, so a single errors.As(err, &snErr) with var snErr *core.ServiceNowError catches any API error when you don't care which class it was.

Reading the platform's error detail

Error() returns the platform's message, and the full payload is available through GetError():

var snErr *core.ServiceNowError
if errors.As(err, &snErr) {
mainErr, _ := snErr.GetError()
message, _ := mainErr.GetMessage()
detail, _ := mainErr.GetDetail()
status, _ := mainErr.GetStatus()
log.Printf("ServiceNow error: message=%v detail=%v status=%v",
message, detail, status)
}

The getters return *string; check for nil before dereferencing — the platform doesn't populate every field for every failure.

Usage errors (sentinels)

Misusing the SDK — calling a method on a nil builder, passing a nil body — returns a sentinel from the errors package. Sentinels compare with errors.Is:

if errors.Is(err, snerrors.ErrNilRequestBuilder) {
// The builder chain was constructed from a nil client.
}

Commonly encountered sentinels:

  • ErrNilRequestBuilder — a builder method was called on a nil builder, usually because client construction failed and the error was ignored.
  • ErrNilRequestAdapter — the client has no request adapter; check the options passed to NewServiceNowServiceClient.
  • ErrNilBody — a Post/Put/Patch was given a nil body.
  • ErrNilContext — a nil context.Context was passed.

Compare sentinels by identity (errors.Is), never by matching the message string — wording isn't part of the compatibility contract.

Retries and 429

The default HTTP pipeline already retries transient failures with backoff (see Configuring the client). If you handle core.TooManyRequestsError yourself, you are seeing a request that exhausted those retries — back off at the workflow level rather than retrying immediately.

Next steps

Was this page helpful?