C.W.K.
Stream
Lesson 03 of 04 · published

Chat, Errors & Multimodal

~14 min · typescript, chat, errors, multimodal

Level 0Spark
0 XP0/35 lessons0/10 achievements
0/140 XP to next level140 XP to go0% complete

Chat sessions in TypeScript

Same shape as Python. ai.chats.create({...}) returns a chat object you call sendMessage on. History is maintained in-memory.

Errors come back as ApiError

The TS SDK wraps API errors in ApiError with name: 'ApiError'. Check by name (since instanceof can be fragile across module boundaries) and read e.status and e.message.

Multimodal uses createUserContent + createPartFromUri

To send images or files alongside text in a single user turn, use the helper functions. They build the right parts structure for you.

Code

Multi-turn chat·typescript
const chat = ai.chats.create({
  model: 'gemini-2.5-flash',
  config: {
    systemInstruction: 'You are a precise but warm tutor.',
  },
});

const r1 = await chat.sendMessage({ message: 'Tell me a fact about octopuses.' });
console.log(r1.text);

const r2 = await chat.sendMessage({ message: 'Now relate that to neural networks.' });
console.log(r2.text);

// Streaming chat
const stream = await chat.sendMessageStream({ message: 'And a haiku?' });
for await (const chunk of stream) {
  if (chunk.text) process.stdout.write(chunk.text);
}
Resilient error handling·typescript
import { ApiError } from '@google/genai';

async function robustGenerate(prompt: string, maxRetries = 4) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await ai.models.generateContent({
        model: 'gemini-2.5-flash',
        contents: prompt,
      });
    } catch (e: any) {
      if (e?.name === 'ApiError') {
        if (e.status === 429 || (e.status >= 500 && e.status < 600)) {
          // Rate limit or server error — back off and retry
          await new Promise(r => setTimeout(r, Math.min(2 ** attempt, 30) * 1000));
          continue;
        }
        // 4xx other than 429 — your fault, don't retry
        throw e;
      }
      throw e;
    }
  }
  throw new Error(`Gave up after ${maxRetries} retries`);
}
Multimodal — image + text·typescript
import { createUserContent, createPartFromUri } from '@google/genai';

// 1. Upload via File API
const uploaded = await ai.files.upload({
  file: 'photo.jpg',
  config: { mimeType: 'image/jpeg' },
});

// 2. Wait for processing if needed
// (large videos go through PROCESSING state)

// 3. Reference in a generateContent call
const response = await ai.models.generateContent({
  model: 'gemini-2.5-flash',
  contents: [
    createUserContent([
      'Describe this image in two sentences:',
      createPartFromUri(uploaded.uri!, uploaded.mimeType!),
    ]),
  ],
});
console.log(response.text);

External links

Exercise

Build a small TypeScript chat CLI: read a line, send it via streaming chat, render to stdout, persist history to a JSONL file. Wrap the API call in robustGenerate so a 429 doesn't kill the loop. Confirm the chat persists across restarts by re-loading from JSONL.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.