Replies: 4 comments 18 replies
-
| Can you provide more details about the polyfill you are using and the backend that sends the response? Is the error reproducible on web when using the polyfill only? | 
Beta Was this translation helpful? Give feedback.
-
| Trying to understand this whole issue better. When you say "For mobile, I had to polyfill TransformStream", what do you mean exactly? what are you targeting and what are the library versions? we have seen people do this successful on Expo for mobile so this is confusing to us, we want to learn what's different | 
Beta Was this translation helpful? Give feedback.
-
| Any updates on this? Not sure my problem is exactly the same, but I'm also using the SDK with an expo-52 project. Everything was working great until I upgraded my version to  This led me to this error: I tried the solution from here. I put that on my  Which makes me think I need to add polyfills to the client to support this. I'm not sure which ones to add though. Happy to clarify anything that could help | 
Beta Was this translation helpful? Give feedback.
-
| If you're stuck on AI SDK 4 like me, I was able to get it to work with polyfills: 
 
 
 Other than standard polyfills recommended by Expo, I need to polyfill ReadableStream with async iterator support and Response with ReadableStream as body. Both are crucial. import { Platform } from "react-native";
import structuredClone from "@ungap/structured-clone";
import { ReadableStream } from "web-streams-polyfill";
const FileList = function() {}
if (Platform.OS !== "web") {
  const setupPolyfills = async () => {
    const { polyfillGlobal } = await import(
      "react-native/Libraries/Utilities/PolyfillFunctions"
    );
    const { TextEncoderStream, TextDecoderStream } = await import(
      "@stardazed/streams-text-encoding"
    );
    if (!("structuredClone" in global)) {
      polyfillGlobal("structuredClone", () => structuredClone);
    }
    polyfillGlobal("TextEncoderStream", () => TextEncoderStream);
    polyfillGlobal("TextDecoderStream", () => TextDecoderStream);
    polyfillGlobal("FileList", () => FileList);
    
    // Polyfill ReadableStream with async iterator support
    polyfillGlobal("ReadableStream", () => {
      // Add async iterator support to ReadableStream prototype
      if (!ReadableStream.prototype[Symbol.asyncIterator]) {
        ReadableStream.prototype.values ??= function({ preventCancel = false } = {}) {
          const reader = this.getReader();
          return {
            async next() {
              try {
                const result = await reader.read();
                if (result.done) {
                  reader.releaseLock();
                }
                return result;
              } catch (e) {
                reader.releaseLock();
                throw e;
              }
            },
            async return(value) {
              if (!preventCancel) {
                const cancelPromise = reader.cancel(value);
                reader.releaseLock();
                await cancelPromise;
              } else {
                reader.releaseLock();
              }
              return { done: true, value };
            },
            [Symbol.asyncIterator]() {
              return this;
            }
          };
        };
        ReadableStream.prototype[Symbol.asyncIterator] ??= ReadableStream.prototype.values;
      }
      
      return ReadableStream;
    });
    
    // Polyfill Response to properly handle ReadableStream as body
    const OriginalResponse = global.Response;
    
    polyfillGlobal("Response", () => {
      class ResponseWithStream extends OriginalResponse {
        constructor(body, init) {
          // If body is a ReadableStream, we need to handle it specially
          if (body && typeof body === 'object' && typeof body.getReader === 'function') {
            // Create a dummy response first
            super(null, init);
            
            // Store the stream as a property
            Object.defineProperty(this, '_bodyStream', {
              value: body,
              writable: false,
              enumerable: false,
              configurable: false
            });
            
            // Override the body getter to return our stream
            Object.defineProperty(this, 'body', {
              get() {
                return this._bodyStream;
              },
              enumerable: true,
              configurable: true
            });
            
            // Override bodyUsed to track if stream has been consumed
            let bodyUsed = false;
            Object.defineProperty(this, 'bodyUsed', {
              get() {
                return bodyUsed;
              },
              set(value) {
                bodyUsed = value;
              },
              enumerable: true,
              configurable: true
            });
            
            console.log('[Response Polyfill] Created Response with ReadableStream body');
          } else {
            // For non-stream bodies, use original behavior
            super(body, init);
          }
        }
        
        // Override methods that consume the body to mark bodyUsed
        async text() {
          if (this._bodyStream) {
            this.bodyUsed = true;
            const reader = this._bodyStream.getReader();
            const chunks = [];
            const decoder = new TextDecoder();
            
            try {
              while (true) {
                const { done, value } = await reader.read();
                if (done) break;
                chunks.push(decoder.decode(value, { stream: true }));
              }
              return chunks.join('');
            } finally {
              reader.releaseLock();
            }
          }
          return super.text();
        }
        
        async json() {
          if (this._bodyStream) {
            const text = await this.text();
            return JSON.parse(text);
          }
          return super.json();
        }
        
        async arrayBuffer() {
          if (this._bodyStream) {
            this.bodyUsed = true;
            const reader = this._bodyStream.getReader();
            const chunks = [];
            
            try {
              while (true) {
                const { done, value } = await reader.read();
                if (done) break;
                chunks.push(value);
              }
              
              // Combine chunks into single ArrayBuffer
              const totalLength = chunks.reduce((acc, chunk) => acc + chunk.byteLength, 0);
              const result = new Uint8Array(totalLength);
              let offset = 0;
              for (const chunk of chunks) {
                result.set(new Uint8Array(chunk), offset);
                offset += chunk.byteLength;
              }
              return result.buffer;
            } finally {
              reader.releaseLock();
            }
          }
          return super.arrayBuffer();
        }
      }
      
      return ResponseWithStream;
    });
  };
  setupPolyfills();
}
export {}; | 
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
The problem
I have streaming working on mobile using the new expo/fetch api (without using the vercel/ai library, just calling endpoints directly)
Then, I implemented my streaming using the vercel/ai library - and this works completely fine in the web context.
For mobile, I had to polyfill TransformStream, since this was marked as missing. But after this, it seemed to almost be working, except I get the error "AI_APICallError: Failed to process successful response".
Debug info
Using the docs, I casted the error to the APICallError type, and printed out some info.
url: http://192.168.1.50:11434/api/chat
requestBodyValues: {"format": undefined, "messages": [{"content": "Hello, how are you?", "role": "user"}], "model": "llama3.2:latest", "options": {"temperature": 0}, "tools": undefined}
statusCode: 200
responseBody: undefined
data: undefined
I've been banging my head against the wall on this issue for a few days - does anyone have any input that might help?
Been testing out combinations of various solutions suggested in existing discussions but no luck: #655 (comment)
Would love to use the vercel/ai library in my React Native project - and I'll make the solution available for anyone to clone.
Thank you SO much in advance - I appreciate any help on this topic
Beta Was this translation helpful? Give feedback.
All reactions