From cf3d97c0c376256f77ab169143df7a040e9e7e7e Mon Sep 17 00:00:00 2001 From: peptiderepo Date: Sat, 18 Apr 2026 10:23:53 +0700 Subject: [PATCH] fix: handle null content in OpenRouter response parser Some models (and the openrouter/free router) may return null content instead of a string. The old check used isset() which returns false for null, triggering the "unexpected response format" error. Now: checks for choices[0].message (not content), treats null content as empty string, and logs the actual response shape on failure so we can diagnose what the model returned. Co-Authored-By: Claude Opus 4.6 --- .../class-open-router-response-parser.php | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/includes/providers/class-open-router-response-parser.php b/includes/providers/class-open-router-response-parser.php index 40e224c..aa5947c 100644 --- a/includes/providers/class-open-router-response-parser.php +++ b/includes/providers/class-open-router-response-parser.php @@ -50,16 +50,37 @@ class PRAutoBlogger_OpenRouter_Response_Parser { * @throws \RuntimeException If response structure is invalid. */ public function parse_success( array $data, string $model ): array { - if ( ! isset( $data['choices'][0]['message']['content'] ) ) { - throw new \RuntimeException( - __( 'OpenRouter returned unexpected response format.', 'prautoblogger' ) + // Validate we have a choices array with at least one message. + if ( ! isset( $data['choices'][0]['message'] ) ) { + // Log the raw shape so we can diagnose what the model actually returned. + PRAutoBlogger_Logger::instance()->error( + sprintf( + 'Unexpected response shape from %s: %s', + $data['model'] ?? $model, + substr( (string) wp_json_encode( $data ), 0, 500 ) + ), + 'openrouter' ); + throw new \RuntimeException( + sprintf( + /* translators: %s: model name */ + __( 'OpenRouter model %s returned unexpected response format.', 'prautoblogger' ), + $data['model'] ?? $model + ) + ); + } + + // Content can be null (some models use tool_calls or refusal instead). + // Treat null as empty string — downstream callers handle empty content. + $content = $data['choices'][0]['message']['content'] ?? ''; + if ( null === $content ) { + $content = ''; } $usage = $data['usage'] ?? []; return [ - 'content' => $data['choices'][0]['message']['content'], + 'content' => (string) $content, 'model' => $data['model'] ?? $model, 'prompt_tokens' => (int) ( $usage['prompt_tokens'] ?? 0 ), 'completion_tokens' => (int) ( $usage['completion_tokens'] ?? 0 ),