Skip to content

Debugging

Overview

When a request does not behave as expected, Instructor provides several inspection points at different levels of detail. Start with the smallest useful tool and escalate as needed.

Quick Inspection Points

Response Envelope

The response() method returns a StructuredOutputResponse that pairs the parsed value with the raw provider response:

$response = (new StructuredOutput())
    ->with(messages: '...', responseModel: User::class)
    ->response();

// The deserialized value
dump($response->value());

// The raw inference response from the provider
dump($response->inferenceResponse());

// Token usage statistics
dump($response->usage());

// Why the model stopped generating
dump($response->finishReason());

Raw Provider Response

If you only need the raw inference response without deserialization metadata:

$raw = (new StructuredOutput())
    ->with(messages: '...', responseModel: User::class)
    ->inferenceResponse();

dump($raw->content());
dump($raw->toolCalls());

Execution Metadata

Access the execution object from a PendingStructuredOutput to inspect the request configuration, attempt history, and output mode:

$pending = (new StructuredOutput())
    ->with(messages: '...', responseModel: User::class)
    ->create();

$execution = $pending->execution();
dump($execution->outputMode());
dump($execution->request());

Streaming Responses

When streaming, use responses() to inspect each emitted StructuredOutputResponse:

$stream = (new StructuredOutput())
    ->with(messages: '...', responseModel: User::class)
    ->stream();

foreach ($stream->responses() as $response) {
    echo $response->isPartial() ? 'partial' : 'final';
    dump($response->value());
}

Runtime Events

For deeper tracing, attach a wiretap to the runtime. This gives you visibility into every internal stage -- schema building, inference calls, extraction, deserialization, validation, and transformation:

$runtime = StructuredOutputRuntime::fromDefaults()
    ->wiretap(fn($event) => $event->print());

$result = (new StructuredOutput($runtime))
    ->with(messages: '...', responseModel: User::class)
    ->get();

For targeted debugging, listen to specific event types:

use Cognesy\Instructor\Events\Response\ResponseValidationFailed;
use Cognesy\Instructor\Events\Response\ResponseDeserializationFailed;
use Cognesy\Instructor\Events\Request\NewValidationRecoveryAttempt;

$runtime = StructuredOutputRuntime::fromDefaults()
    ->onEvent(ResponseValidationFailed::class, fn($e) => dump('Validation failed:', $e->data))
    ->onEvent(ResponseDeserializationFailed::class, fn($e) => dump('Deserialization failed:', $e->data))
    ->onEvent(NewValidationRecoveryAttempt::class, fn($e) => dump('Retrying...', $e->data));

See the Events page for the full list of available event classes.

JSON Output Inspection

When you need to see the raw JSON that came back from the provider (before deserialization), use the toJson() or toArray() methods on PendingStructuredOutput:

$pending = (new StructuredOutput())
    ->with(messages: '...', responseModel: User::class)
    ->create();

// Raw JSON string
echo $pending->toJson();

// Parsed array
dump($pending->toArray());

Common Debugging Scenarios

Wrong or Missing Fields

Check that your response model class has the correct property types and names. Use wiretap() to inspect the raw JSON from the provider and compare it with your schema expectations.

Validation Failures Exhausting Retries

Listen for ResponseValidationFailed events to see exactly which validation rules are failing. Consider increasing maxRetries or relaxing validation constraints.

Unexpected Deserialization Errors

Listen for ResponseDeserializationFailed to see the raw data that could not be mapped to your class. This often reveals type mismatches between the schema and the actual model response.

Streaming Not Producing Updates

Verify that withStreaming(true) is set and that you are consuming the stream (e.g., iterating partials() or calling finalValue()). The stream is lazy -- no data flows until you start reading.