> For the complete documentation index, see [llms.txt](https://docs.licensespring.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.licensespring.com/sdks/go-sdk/v2/error-handling-and-logging.md).

# 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:

```go
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()`.

```go
// 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:

```go
if !ld.IsFloatingorFloatingCloud() {
	return ld, errors.LicenseNotFloating(string(debug.Stack()))
}
```

All such error constructors follow the format:

```go
func LicenseNotFloating(stack ...string) *Error {
	e := &Error{
		Status:  http.StatusBadRequest,
		Code:    "license_not_floating",
		Message: "the license is not a floating license",
	}
	if len(stack) > 0 {
		e.StackTrace = stack[0]
	}
	return e
}
```

#### LicenseHandler Layer: Logging and Safe Return

{% stepper %}
{% step %}

### Log the complete error

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

Example:

```go
ld, err := lh.LicenseManager.ReleaseFloatingLicense(ctx)
if err != nil {
	logger.Error(fmt.Errorf("floating license release failed: %w", err))
	return ld, errors.SanitizeError(err)
}
```

{% endstep %}

{% step %}

### Return a sanitized error

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

The `SanitizeError` function:

```go
func SanitizeError(err *errors.Error) map[string]interface{} {
	return map[string]interface{}{
		"status":  err.Status,
		"code":    err.Code,
		"message": err.Message,
	}
}
```

{% endstep %}
{% endstepper %}

#### Example: Full Error Flow for Floating License Release

```go
func (ls *LicenseManager) ReleaseFloatingLicense(ctx context.Context) (*types.LicenseData, error) {
	if err := ls.validate(); err != nil {
		// Validation errors are returned immediately, no need to wrap.
		return nil, err
	}
	ld := ls.LicenseData

	isActive, isEnabled, isExpired := ls.CheckLicenseStatus()

	// This is the lowest level where the internal SDK error occurs.
	// We attach a stack trace using debug.Stack() and create a 
	// custom *Error object.
	if !isEnabled {
		return ld, errors.LicenseNotEnabled(string(debug.Stack()))
	}
	if !isActive {
		return ld, errors.LicenseNotActive(string(debug.Stack()))
	}
	if isExpired {
		return ld, errors.LicenseExpired(string(debug.Stack()))
	}

	if !ld.IsFloatingorFloatingCloud() {
		return ld, errors.LicenseNotFloating(string(debug.Stack()))
	}

	config := ls.LicenseClient.LicenseClientConfiguration
	authData := ld.AuthenticationData

	request := core_request.LicenseRequest{
		HardwareId: config.HardwareId,
		Product:    config.ProductCode,
		Auth:       authData,
	}

	// This error comes from the server.
	// The LicenseClient layer has already captured the stack trace.
	// We return it as-is so that the outer layer (LicenseHandler)
	// can log and sanitize it.
	err := ls.LicenseClient.FloatingRelease(ctx, request)
	if err != nil {
		return ld, err
	}

	ld.RevokeFloatingLicense()

	logger.Info("Floating License released successfully")
	return ld, nil
}
```

```go
func (lh *LicenseHandler) ReleaseFloatingLicense(ctx context.Context) (*types.LicenseData, error) {
	ld, err := lh.LicenseManager.ReleaseFloatingLicense(ctx)
	if err != nil {
		// We do all logging at the outermost layer. 
		logger.Error(fmt.Errorf("license floating release failed: %w", err))

		// We return a sanitized version of the error to avoid leaking internals.
		return ld, errors.SanitizeError(err)
	}

	s := lh.Storage
	err = s.SaveLicense(ld)
	if err != nil {
		logger.Error(fmt.Errorf("license floating release failed: %w", err))
		return nil, errors.SanitizeError(err)
	}

	return ld, nil
}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.licensespring.com/sdks/go-sdk/v2/error-handling-and-logging.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
