> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zespan.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Finish outcome reference

> How Zespan records how a model call ended: the finish value, the cut-off, blocked, refused, empty and stream-cut flags, and the failure kind. Fixed values only, no text.

Zespan records how each model call ended in a small, fixed set of fields on the span, from your SDK or from OpenTelemetry attributes. The fields hold enums, yes/no flags, numbers and short codes only: never prompt or answer text, never an error message. That is why they are available with content capture off. Anything that does not match the rules below is dropped (the rest of the span is kept).

They drive the **chips** on traces and spans (see [How a call ended](https://docs.zespan.com/dashboard/traces.md#how-a-call-ended-finish-outcomes)), the **Outcome** filter on the traces list, the alert metrics `truncated_rate`, `blocked_rate` and `empty_response_rate` (see [Alerts](https://docs.zespan.com/dashboard/alerts.md)), and the causes shown on issues and incidents.

## Fields

| Field | Values | Meaning |
| - | - | - |
| `finish_normalized` | `stop`, `length`, `tool_calls`, `content_filter`, `refusal`, `error`, `other`, `unknown` | The finish reason, the same across providers |
| `finish_raw` | a short code (letters, digits, `_ . : -`, up to 64 characters) | The provider's own finish value. Free text is dropped, never rewritten |
| `truncated` | `true` | The answer was cut off at the output length limit |
| `blocked` | `true` | The answer was blocked |
| `block_source` | `provider_output`, `provider_prompt`, `zespan_guardrail` | Who blocked it |
| `refusal` | `true` | The model declined to answer |
| `empty_response` | `true` | No answer and no tool call |
| `finish_missing` | `true` | A stream ended without a finish signal |
| `incomplete`, `finish_error` | `true` | The provider reported an incomplete or errored finish |
| `error_kind` | `rate_limit`, `quota`, `timeout`, `network`, `auth`, `invalid_request`, `context_length`, `content_filter`, `model_not_found`, `not_found`, `overloaded`, `server_error`, `cancelled`, `guardrail_blocked`, `tool_error`, `unknown` | Why a failed call failed |
| `error_class`, `provider_code` | a short code | The error class name and the provider's error code |
| `http_status` | 100 to 599 | The HTTP status of a failed call |
| `retryable`, `retry_after_ms`, `attempt` | `true`, 0 to 86,400,000, 0 to 1,000 | Retry facts |

Which chip a call gets:

| Chip | Condition |
| - | - |
| **Truncated** | `truncated` |
| **Blocked** | `blocked`, unless `block_source` is `zespan_guardrail` |
| **Refusal** | `refusal` |
| **Empty response** | `empty_response` |
| **Stream cut** | `finish_missing` |

## Causes

When one of these outcomes is the only evidence a failed or degraded call has, Zespan names the cause and gives fix steps. The cause keys are `output_truncated`, `provider_refusal`, `empty_response` and `stream_incomplete`. "Maximum tokens reached" is always an output truncation, never a context-length error.

## Next steps

* [Traces](https://docs.zespan.com/dashboard/traces.md#how-a-call-ended-finish-outcomes) — the chips, the Outcome filter, and the fix for each outcome
* [Alerts](https://docs.zespan.com/dashboard/alerts.md) — alert on the rate of cut-off, blocked and empty answers
* [OpenTelemetry integration](https://docs.zespan.com/sdk/otel-integration.md#finish-and-failure-outcome) — the attributes read when you send OTel spans directly
* [Silent-failure coverage](https://docs.zespan.com/reference/silent-failure-coverage.md) — the silent failures Zespan looks for and what each check needs


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.