Error Handling and Logging

In our SDK, we divide all errors into two primary categories:

  1. Server Errors – returned directly from the LicenseSpring backend.

  2. Internal SDK Errors – generated within the SDK itself.

Error Type

We define a unified error type for all internal and server errors:

type Error struct {
	Status     int                    `json:"status"`
	Code       string                 `json:"code"`
	Message    string                 `json:"message"`
	Details    map[string]interface{} `json:"details,omitempty"`
	Inner      error                  `json:"-"`
	StackTrace string                 `json:"-"`
}

Server-Originated Errors

These errors are returned directly from the LicenseSpring API. We do not wrap them multiple times. Instead, we handle them as follows:

  • At the lowest point where the API response is received, we wrap the error and add a stack trace using debug.Stack().

// resp is the server's response
if resp.Error != nil {			
     err := core_errors.Error{
			Status: resp.Error.Status,
			Code: resp.Error.Code,
			Message: resp.Error.Message,
			Inner: resp.Error,
			StackTrace: string(debug.Stack())
		}
  • These errors are returned as-is up the call chain.

  • Logging occurs only once, at the LicenseHandler level.

Internal SDK Errors

For errors caused by logic inside the SDK, we have predefined constructor functions. These are always called at the lowest layer where the error originates, and a stack trace is attached.

Example:

All such error constructors follow the format:

LicenseHandler Layer: Logging and Safe Return

1

Log the complete error

At the topmost layer (usually inside LicenseHandler), log the complete error including stack trace.

Example:

2

Return a sanitized error

Return a safe version of the error (excluding stack and internal details) to avoid leaking internals.

The SanitizeError function:

Example: Full Error Flow for Floating License Release

Was this helpful?