Skip to content

Extension crash PHP (assert in Zend) during Llm::complete() #2

@KlimTodrik

Description

@KlimTodrik

llm PHP extension crashes PHP (assert in Zend) during Llm::complete() — “Exceptions must implement Throwable” / follow-up LLMException at shutdown

Environment:

  • Product: Manticore Buddy (Swoole runtime), plugin uses llm PHP extension
  • PHP 8.4.17 (cli) (built: Feb 6 2026 01:17:54) (NTS DEBUG)
  • OS/container: (test-kit)
  • LLM provider/model: openai:gpt-3.5-turbo (model id string: openai:gpt-3.5-turbo)

Summary:

Calling Llm::complete() can crash the PHP process (exit code 134) with an assertion in Zend/zend_exceptions.c:
Assertion 'instanceof_function(exception_ce, zend_ce_throwable) && "Exceptions must implement Throwable"' failed.
In the real app (Swoole worker), this manifests as Fatal error: Uncaught exception LLMException in Unknown on line 0 during shutdown / request end.

Impact:

  • Hard crash of PHP process / Swoole worker
  • Not catchable from userland try/catch (Throwable $e)
  • Leaves application in inconsistent state (task result not set, request handler returns wrong error)

Reproduction (Minimal):

Run inside a PHP environment where the llm extension is available.

<?php declare(strict_types=1);

  function padToLength(string $text, int $targetLength): string {
      $len = strlen($text);
      if ($len >= $targetLength) return $text;
      $padLen = $targetLength - $len;
      return $text . "\n\nPAD:" . str_repeat('x', max(0, $padLen - 6));
  }

  function doComplete(\Llm $llm, string $label, string $prompt, float $temperature, int $maxTokens): void {
      echo "== {$label}\n";
      echo "prompt_length=" . strlen($prompt) . "\n";
      $llm->setTemperature($temperature);
      $llm->setMaxTokens($maxTokens);
      $messages = [\Message::user($prompt)];

      echo "complete() start\n";
      try {
          $response = $llm->complete($messages);
          echo "complete() ok\n";
          echo "finish_reason=" . $response->getFinishReason() . "\n";
          $usage = $response->getUsage();
          echo "tokens_total=" . $usage->getTotalTokens() . " prompt=" . $usage->getPromptTokens() . " output=" . $usage->getOutputTokens() . "\n";
      } catch (\Throwable $e) {
          echo "complete() caught: " . $e::class . ": " . $e->getMessage() . "\n";
      }
      echo "complete() end\n";
  }

  $modelId = 'openai:gpt-3.5-turbo';
  $llm = new \Llm($modelId);

  // Prompts sized to match production logs (might matter)
  $intentPrompt = padToLength("intent prompt\n", 1736);
  $queryGenPrompt = padToLength("query generation prompt\n", 2661);
  $expansionPrompt = padToLength("expansion intent prompt\n", 1455);

  doComplete($llm, 'intent_classification', $intentPrompt, 0.1, 50);
  doComplete($llm, 'query_generation', $queryGenPrompt, 0.3, 200);

  // This call crashes PHP with Zend assertion:
  doComplete($llm, 'expansion_intent', $expansionPrompt, 0.1, 10);

  echo "done\n";

Observed result:

  • First two calls: complete() ok
  • Third call: PHP process aborts (exit code 134) with:
  php: .../Zend/zend_exceptions.c:857: zend_throw_exception_zstr: Assertion `instanceof_function(exception_ce, zend_ce_throwable) && "Exceptions must implement Throwable"' failed.

Notes / extra context:

  • In a Swoole worker (single-process, long-lived), this causes worker crashes at request shutdown with:
    Fatal error: Uncaught exception LLMException in Unknown on line 0
  • The crash happens after complete() start and before any userland catch/finally can run, suggesting the extension raises an invalid exception or corrupts exception state internally.
  • Reproduces both when running only the “expansion intent” call and when doing 2 successful completes first and crashing on the 3rd.

Script output

== intent_classification
prompt_length=1736
complete() start
complete() ok
finish_reason=stop
tokens_total=227 prompt=227 output=0
complete() end
== query_generation
prompt_length=2661
complete() start
complete() ok
finish_reason=stop
tokens_total=343 prompt=343 output=0
complete() end
== expansion_intent
prompt_length=1455
complete() start
php: /home/runner/work/executor/executor/build/Zend/zend_exceptions.c:857: zend_throw_exception_zstr: Assertion `instanceof_function(exception_ce, zend_ce_throwable) && "Exceptions must implement Throwable"' failed.
Aborted

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions