Backward compatibility and breaking changes

I have a question following an upgrade from ai@5.0.86 to ai@5.0.93.

I am building a javascript library that I can share with some customer, this library contains the AI SDK.

This weekend I upgraded our backend (also using ai sdk) to use ai@5.0.93, since then all the previous version of my package broke because of the finish reason that has been added:

This is the first time I’m noticing that with this package, but it’s worrying me a bit as testing all pairs of client/server would be infeasible with the short release cycle and continuous improvement of this library.

Was this done on purpose ? Or is it something that went unnoticed and that broke the compatibility with older client (which is kind of an edge case)

Hi @ex0ns, welcome to the Vercel Community!

Thanks for raising this query here.

I’ve tried developing libraries before and I agree full testing on each release is infeasible.

I want to understand how adding a new field broke existing code. Could you share an example code or repository to help us understand?

Since, it has a proper changelog I can say this release was intentional.

I unfortunately don’t have a reproducer I can easily extract, but the API was sending a response with {”type”: “finish”, “finishReason”: "stop”} and this was crashing the frontend (still using version 5.0.86)

436.3fe587091bcb2bda.js:3 AI_TypeValidationError: Type validation failed: Value: {“type”:“finish”,“finishReason”:“stop”}.

Error message: [
{“code”: “invalid_union”,“errors”: [[{“code”: “invalid_value”,“values”: [“text-start”],“path”: [“type”],“message”: “Invalid input: expected “text-start””},{“expected”: “string”,“code”: “invalid_type”,“path”: [“id”],“message”: “Invalid input: expected string, received undefined”},{“code”: “unrecognized_keys”,“keys”: [“finishReason”],“path”: ,“message”: “Unrecognized key: “finishReason””}],[{“code”: “invalid_value”,“values”: [“text-delta”],“path”: [“type”],“message”: “Invalid input: expected “text-delta””},{“expected”: “string”,“code”: “invalid_type”,“path”: [“id”],“message”: “Invalid input: expected string, received undefined”},{“expected”: “string”,“code”: “invalid_type”,“path”: [“delta”],“message”: “Invalid input: expected string, received undefined”},{“code”: “unrecognized_keys”,“keys”: [“finishReason”],“path”: ,“message”: “Unrecognized key: “finishReason””}],[{“code”: “invalid_value”,“values”: [“text-end”],“path”: [“type”],“message”: “Invalid input: expected “text-end””},{“expected”: “string”,“code”: “invalid_type”,“path”: [“id”],“message”: “Invalid input: expected string, received undefined”},{“code”: “unrecognized_keys”,“keys”: [“finishReason”],“path”: ,“message”: “Unrecognized key: “finishReason””}],[{“code”: “invalid_value”,“values”: [“error”],“path”: [“type”],“message”: “Invalid input: expected “error””},{“expected”: “string”,“code”: “invalid_type”,“path”: [“errorText”],“message”: “Invalid input: expected string, received undefined”},{“code”: “unrecognized_keys”,“keys”: [“finishReason”],“path”: ,“message”: “Unrecognized key: “finishReason””}],[{“code”: “invalid_value”,“values”: [“tool-input-start”],“path”: [“type”],“message”: “Invalid input: expected “tool-input-start””},{“expected”: “string”,“code”: “invalid_type”,“path”: [“toolCallId”],“message”: “Invalid input: expected string, received undefined”},{“expected”: “string”,“code”: “invalid_type”,“path”: [“toolName”],“message”: “Invalid input: expected string, received undefined”},{“code”: “unrecognized_keys”,“keys”: [“finishReason”],“path”: ,“message”: “Unrecognized key: “finishReason””}],[{“code”: “invalid_value”,“values”: [“tool-input-delta”],“path”: [“type”],“message”: “Invalid input: expected “tool-input-delta””},{“expected”: “string”,“code”: “invalid_type”,“path”: [“toolCallId”],“message”: “Invalid input: expected string, received undefined”},{“expected”: “string”,“code”: “invalid_type”,“path”: [“inputTextDelta”],“message”: “Invalid input: expected string, received undefined”},{“code”: “unrecognized_keys”,“keys”: [“finishReason”],“path”: ,“message”: “Unrecognized key: “finishReason””}],[{“code”: “invalid_value”,“values”: [“tool-input-available”],“path”: [“type”],“message”: “Invalid input: expected “tool-input-available””},{“expected”: “string”,“code”: “invalid_type”,“path”: [“toolCallId”],“message”: “Invalid input: expected string, received undefined”},{“expected”: “string”,“code”: “invalid_type”,“path”: [“toolName”],“message”: “Invalid input: expected string, received undefined”},{“code”: “unrecognized_keys”,“keys”: [“finishReason”],“path”: ,“message”: “Unrecognized key: “finishReason””}],[{“code”: “invalid_value”,“values”: [“tool-input-error”],“path”: [“type”],“message”: “Invalid input: expected “tool-input-error””},{“expected”: “string”,“code”: “invalid_type”,“path”: [“toolCallId”],“message”: “Invalid input: expected string, received undefined”},{“expected”: “string”,“code”: “invalid_type”,“path”: [“toolName”],“message”: “Invalid input: expected string, received undefined”},{“expected”: “string”,“code”: “invalid_type”,“path”: [“errorText”],“message”: “Invalid input: expected string, received undefined”},{“code”: “unrecognized_keys”,“keys”: [“finishReason”],“path”: ,“message”: “Unrecognized key: “finishReason””}],[{“code”: “invalid_value”,“values”: [“tool-output-available”],“path”: [“type”],“message”: “Invalid input: expected “tool-output-available””},{“expected”: “string”,“code”: “invalid_type”,“path”: [“toolCallId”],“message”: “Invalid input: expected string, received undefined”},{“code”: “unrecognized_keys”,“keys”: [“finishReason”],“path”: ,“message”: “Unrecognized key: “finishReason””}],[{“code”: “invalid_value”,“values”: [“tool-output-error”],“path”: [“type”],“message”: “Invalid input: expected “tool-output-error””},{“expected”: “string”,“code”: “invalid_type”,“path”: [“toolCallId”],“message”: “Invalid input: expected string, received undefined”},{“expected”: “string”,“code”: “invalid_type”,“path”: [“errorText”],“message”: “Invalid input: expected string, received undefined”},{“code”: “unrecognized_keys”,“keys”: [“finishReason”],“path”: ,“message”: “Unrecognized key: “finishReason””}],[{“code”: “invalid_value”,“values”: [“reasoning-start”],“path”: [“type”],“message”: “Invalid input: expected “reasoning-start””},{“expected”: “string”,“code”: “invalid_type”,“path”: [“id”],“message”: “Invalid input: expected string, received undefined”},{“code”: “unrecognized_keys”,“keys”: [“finishReason”],“path”: ,“message”: “Unrecognized key: “finishReason””}],[{“code”: “invalid_value”,“values”: [“reasoning-delta”],“path”: [“type”],“message”: “Invalid input: expected “reasoning-delta””},{“expected”: “string”,“code”: “invalid_type”,“path”: [“id”],“message”: “Invalid input: expected string, received undefined”},{“expected”: “string”,“code”: “invalid_type”,“path”: [“delta”],“message”: “Invalid input: expected string, received undefined”},{“code”: “unrecognized_keys”,“keys”: [“finishReason”],“path”: ,“message”: “Unrecognized key: “finishReason””}],[{“code”: “invalid_value”,“values”: [“reasoning-end”],“path”: [“type”],“message”: “Invalid input: expected “reasoning-end””},{“expected”: “string”,“code”: “invalid_type”,“path”: [“id”],“message”: “Invalid input: expected string, received undefined”},{“code”: “unrecognized_keys”,“keys”: [“finishReason”],“path”: ,“message”: “Unrecognized key: “finishReason””}],[{“code”: “invalid_value”,“values”: [“source-url”],“path”: [“type”],“message”: “Invalid input: expected “source-url””},{“expected”: “string”,“code”: “invalid_type”,“path”: [“sourceId”],“message”: “Invalid input: expected string, received undefined”},{“expected”: “string”,“code”: “invalid_type”,“path”: [“url”],“message”: “Invalid input: expected string, received undefined”},{“code”: “unrecognized_keys”,“keys”: [“finishReason”],“path”: ,“message”: “Unrecognized key: “finishReason””}],[{“code”: “invalid_value”,“values”: [“source-document”],“path”: [“type”],“message”: “Invalid input: expected “source-document””},{“expected”: “string”,“code”: “invalid_type”,“path”: [“sourceId”],“message”: “Invalid input: expected string, received undefined”},{“expected”: “string”,“code”: “invalid_type”,“path”: [“mediaType”],“message”: “Invalid input: expected string, received undefined”},{“expected”: “string”,“code”: “invalid_type”,“path”: [“title”],“message”: “Invalid input: expected string, received undefined”},{“code”: “unrecognized_keys”,“keys”: [“finishReason”],“path”: ,“message”: “Unrecognized key: “finishReason””}…

This is too long to fit inside a single message.
Here is the network output from the event stream:

image

Edit: sorry for the formatting, the error is very very long

I see. Are you using generateObject? Can you share the code snippet for the actual ai-sdk use?

We are using sendMessage, that we wraps because of some custom authentication

  const { chat, isLoadingMessages } = useSharedChat(flowId);
  const coreClient = useCoreClient();

  const sendMessageWithAuth = async (
    message: Parameters<typeof chat.sendMessage>[0],
    chatRequestOptions?: ChatRequestOptions,
  ) => {
    /* include our authentcation token in the headers, then the default headers, then the query specific headers */
    const requestHeaders = {
      ...(await coreClient.getHeaders()),
      ...chatRequestOptions?.headers,
    };

    /* always include the headers, and forward the rest of the options */
    chatRequestOptions = {
      ...chatRequestOptions,
      headers: requestHeaders,
    };

    await chat.sendMessage(message, chatRequestOptions);
  };

Here useSharedChat is wrapping useChat On the server we have quite a complex setup merging different streams, but this is all derived from a streamText call