koog
koog copied to clipboard
Unexpected 'null' value instead of string literal at path: $.choices[0].finishReason
When using some models in OpenRouter, the following error may occur. It is certain that https://openrouter.ai/qwen/qwen3-235b-a22b-2507
[DefaultDispatcher-worker-2] ERROR ai.koog.agents.core.agent.AIAgent - [agent id: 882f9190-b9b2-4c17-887d-7037fef422f3, run id: c8b11a22-e87f-439e-a544-40daec91138a] Reporting problem: Unexpected JSON token at offset 339: Unexpected 'null' value instead of string literal at path: $.choices[0].finishReason
JSON input: .....obs":null,"finish_reason":null,"native_finish_reason":null,".....
kotlinx.serialization.json.internal.JsonDecodingException: Unexpected JSON token at offset 339: Unexpected 'null' value instead of string literal at path: $.choices[0].finishReason
JSON input: .....obs":null,"finish_reason":null,"native_finish_reason":null,".....
at kotlinx.serialization.json.internal.JsonExceptionsKt.JsonDecodingException(JsonExceptions.kt:24)
at kotlinx.serialization.json.internal.JsonExceptionsKt.JsonDecodingException(JsonExceptions.kt:32)
at kotlinx.serialization.json.internal.AbstractJsonLexer.fail(AbstractJsonLexer.kt:587)
at kotlinx.serialization.json.internal.AbstractJsonLexer.fail$default(AbstractJsonLexer.kt:585)
at kotlinx.serialization.json.internal.AbstractJsonLexer.consumeStringLenientNotNull(AbstractJsonLexer.kt:430)
at kotlinx.serialization.json.internal.StreamingJsonDecoder.decodeString(StreamingJsonDecoder.kt:338)
at kotlinx.serialization.encoding.AbstractDecoder.decodeStringElement(AbstractDecoder.kt:58)
at ai.koog.prompt.executor.clients.openai.base.models.OpenAIChoice$$serializer.deserialize(OpenAIDataModels.kt:738)
In Koog source code, the nullability of finishReason is inconsistent. Below is the current data class.
@Serializable
public class OpenAIStreamChoice(
public val delta: OpenAIStreamDelta,
public val finishReason: String? = null,
public val index: Int,
public val logprobs: OpenAIChoiceLogProbs? = null,
)
@Serializable
public class OpenAIChoice(
public val finishReason: String,
public val index: Int,
public val logprobs: OpenAIChoiceLogProbs? = null,
public val message: OpenAIMessage,
)
- In OpenAI the finishReason is required field (proof)
- In openRouter it is optional https://openrouter.ai/docs/api-reference/overview#completionsresponse-format.
Would it make sense to create OpenRouterChoice object for open router instead of using OpenAIChoice from base openai models?
- In OpenAI the finishReason is required field (proof)
- In openRouter it is optional https://openrouter.ai/docs/api-reference/overview#completionsresponse-format.
Would it make sense to create OpenRouterChoice object for open router instead of using OpenAIChoice from base openai models?
Actually, many providers are not truly compatible with OpenAI's API, although they claim to be. To reduce the workload, I think creating a more lenient OpenAI-compatible client might also be a method.
Hello , I want to inform you about an issue I encountered during deserialization of the OpenAI API response. The field finish_reason was sometimes returned as null, but our model was expecting a non-nullable String. Resolution: I updated the data model to handle finish_reason as a nullable field with proper @SerialName mapping. This ensures the application no longer fails when the API returns null.
@Serializable data class OpenAIChoice( @SerialName("finish_reason") val finishReason: String? = null )
The issue has been resolved and everything is functioning as expected now.
Best regards, Mohit Anuragi