# Error handling

> For the complete documentation index, see [llms.txt](https://docs.temporal.io/llms.txt).
> Any documentation page is available as raw Markdown by appending `.md` to its URL.
> For related questions, query https://docs.temporal.io/api/retrieve?q=<question>&jtb=<job-to-be-done>&src=md-page&agent=<your-name> (no auth; returns JSON `{content, source_url, page_url}`, with `source_url` pointing to Markdown for docs pages). Don't include personal data or credentials.

> How to handle errors using the Temporal Go SDK.

## Error handling 

Within a Workflow, an Activity or Child Workflow execution might fail. You can
handle errors differently based on the error type.

If the Activity returns an error as `errors.New()` or `fmt.Errorf()`, that error is converted into `*temporal.ApplicationError`.

If the Activity returns an error as `temporal.NewNonRetryableApplicationError("error message", "ErrorType", cause, details)`, that error is returned as `*temporal.ApplicationError`.

There are other types of errors such as `*temporal.TimeoutError`, `*temporal.CanceledError` and
`*temporal.PanicError`.

Here's an example of handling Activity errors within Workflow code that differentiates between different error types.

<!--SNIPSTART go-error-handling-activity-errors-->
[go-sandbox/bestpractices/errorhandling/workflow.go](https://github.com/temporalio/documentation-sdk-code-examples/blob/main/go-sandbox/bestpractices/errorhandling/workflow.go)
```go
err := workflow.ExecuteActivity(ctx, YourActivity, failure).Get(ctx, nil)
if err != nil {
	var applicationErr *temporal.ApplicationError
	if errors.As(err, &applicationErr) {
		// retrieve error message
		workflow.GetLogger(ctx).Info("Application error", "error", applicationErr.Error())

		// handle Activity errors (created via NewApplicationError() API)
		var detailMsg string // assuming Activity return error by NewApplicationError("message", "ErrType", "string details")
		if err := applicationErr.Details(&detailMsg); err != nil { // extract strong typed details
			workflow.GetLogger(ctx).Warn("Unable to decode error details", "error", err)
		}

		// handle Activity errors (errors created other than using NewApplicationError() API)
		switch applicationErr.Type() {
		case "CustomErrTypeA":
			// handle CustomErrTypeA
			outcome = "CustomErrTypeA: " + detailMsg
		case CustomErrTypeB:
			// handle CustomErrTypeB
			outcome = "CustomErrTypeB: " + detailMsg
		default:
			// newer version of Activity could return new errors that Workflow was not aware of.
			outcome = "unknown application error"
		}
	}

	var canceledErr *temporal.CanceledError
	if errors.As(err, &canceledErr) {
		// handle cancellation
		outcome = "canceled"
	}

	var timeoutErr *temporal.TimeoutError
	if errors.As(err, &timeoutErr) {
		// handle timeout, could check timeout type by timeoutErr.TimeoutType()
		switch timeoutErr.TimeoutType() {
		case enumspb.TIMEOUT_TYPE_SCHEDULE_TO_START:
			// Handle ScheduleToStart timeout.
			outcome = "ScheduleToStart timeout"
		case enumspb.TIMEOUT_TYPE_START_TO_CLOSE:
			// Handle StartToClose timeout.
			outcome = "StartToClose timeout"
		case enumspb.TIMEOUT_TYPE_HEARTBEAT:
			// Handle heartbeat timeout.
			outcome = "Heartbeat timeout"
		default:
		}
	}

	var panicErr *temporal.PanicError
	if errors.As(err, &panicErr) {
		// handle panic, message and call stack are available by panicErr.Error() and panicErr.StackTrace()
		outcome = "panic"
	}
}
```
<!--SNIPEND-->

### Panics and deferred functions

In Go, [`defer` schedules cleanup functions and `recover()` catches panics](https://go.dev/blog/defer-panic-and-recover) to prevent them from crashing the program.
This doesn't work the same way in Temporal Workflow code — you cannot `recover()` from a panic inside a `defer`.
Deferred functions that try to interact with the Temporal SDK during panic unwinding will re-panic immediately.

Use `defer` only for local cleanup.
Handle Temporal API cleanup through explicit error checks instead.
