home / skills / jezweb / claude-skills / openai-assistants
/skills/openai-assistants
This skill helps maintain legacy OpenAI Assistants v2 apps, troubleshoot threads, and manage vector stores with guided, safe migration insights.
npx playbooks add skill jezweb/claude-skills --skill openai-assistantsReview the files below or copy the command above to add this skill to your agents.
---
name: openai-assistants
description: |
Build stateful chatbots with OpenAI Assistants API v2 - Code Interpreter, File Search (10k files), Function Calling. Prevents 10 documented errors including vector store upload bugs, temperature parameter conflicts, memory leaks. Deprecated (sunset August 2026); use openai-responses for new projects.
Use when: maintaining legacy chatbots, implementing RAG with vector stores, or troubleshooting thread errors, vector store delays, uploadAndPoll issues.
user-invocable: true
---
# OpenAI Assistants API v2
**Status**: Production Ready (⚠️ Deprecated - Sunset August 26, 2026)
**Package**: [email protected]
**Last Updated**: 2026-01-21
**v1 Deprecated**: December 18, 2024
**v2 Sunset**: August 26, 2026 (migrate to Responses API)
---
## ⚠️ Deprecation Notice
**OpenAI is deprecating Assistants API in favor of [Responses API](../openai-responses/SKILL.md).**
**Timeline**: v1 deprecated Dec 18, 2024 | v2 sunset August 26, 2026
**Use this skill if**: Maintaining legacy apps or migrating existing code (12-18 month window)
**Don't use if**: Starting new projects (use `openai-responses` skill instead)
**Migration**: See `references/migration-to-responses.md`
---
## Quick Start
```bash
npm install [email protected]
```
```typescript
import OpenAI from 'openai';
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
// 1. Create assistant
const assistant = await openai.beta.assistants.create({
name: "Math Tutor",
instructions: "You are a math tutor. Use code interpreter for calculations.",
tools: [{ type: "code_interpreter" }],
model: "gpt-5",
});
// 2. Create thread
const thread = await openai.beta.threads.create();
// 3. Add message
await openai.beta.threads.messages.create(thread.id, {
role: "user",
content: "Solve: 3x + 11 = 14",
});
// 4. Run assistant
const run = await openai.beta.threads.runs.create(thread.id, {
assistant_id: assistant.id,
});
// 5. Poll for completion
let status = await openai.beta.threads.runs.retrieve(thread.id, run.id);
while (status.status !== 'completed') {
await new Promise(r => setTimeout(r, 1000));
status = await openai.beta.threads.runs.retrieve(thread.id, run.id);
}
// 6. Get response
const messages = await openai.beta.threads.messages.list(thread.id);
console.log(messages.data[0].content[0].text.value);
```
---
## Core Concepts
**Four Main Objects:**
1. **Assistants**: Configured AI with instructions (max 256k chars in v2, was 32k in v1), model, tools, metadata
2. **Threads**: Conversation containers with persistent message history (max 100k messages)
3. **Messages**: User/assistant messages with optional file attachments
4. **Runs**: Async execution with states (queued, in_progress, requires_action, completed, failed, expired)
---
## Key API Patterns
### Assistants
```typescript
const assistant = await openai.beta.assistants.create({
model: "gpt-5",
instructions: "System prompt (max 256k chars in v2)",
tools: [{ type: "code_interpreter" }, { type: "file_search" }],
tool_resources: { file_search: { vector_store_ids: ["vs_123"] } },
});
```
**Key Limits**: 256k instruction chars (v2), 128 tools max, 16 metadata pairs
### Threads & Messages
```typescript
// Create thread with messages
const thread = await openai.beta.threads.create({
messages: [{ role: "user", content: "Hello" }],
});
// Add message with attachments
await openai.beta.threads.messages.create(thread.id, {
role: "user",
content: "Analyze this",
attachments: [{ file_id: "file_123", tools: [{ type: "code_interpreter" }] }],
});
// List messages
const msgs = await openai.beta.threads.messages.list(thread.id);
```
**Key Limits**: 100k messages per thread
---
### Runs
```typescript
// Create run with optional overrides
const run = await openai.beta.threads.runs.create(thread.id, {
assistant_id: "asst_123",
additional_messages: [{ role: "user", content: "Question" }],
max_prompt_tokens: 1000,
max_completion_tokens: 500,
});
// Poll until complete
let status = await openai.beta.threads.runs.retrieve(thread.id, run.id);
while (['queued', 'in_progress'].includes(status.status)) {
await new Promise(r => setTimeout(r, 1000));
status = await openai.beta.threads.runs.retrieve(thread.id, run.id);
}
```
**Run States**: `queued` → `in_progress` → `requires_action` (function calling) / `completed` / `failed` / `cancelled` / `expired` (10 min max)
---
### Streaming
```typescript
const stream = await openai.beta.threads.runs.stream(thread.id, { assistant_id });
for await (const event of stream) {
if (event.event === 'thread.message.delta') {
process.stdout.write(event.data.delta.content?.[0]?.text?.value || '');
}
}
```
**Key Events**: `thread.run.created`, `thread.message.delta` (streaming content), `thread.run.step.delta` (tool progress), `thread.run.completed`, `thread.run.requires_action` (function calling)
---
## Tools
### Code Interpreter
Runs Python code in sandbox. Generates charts, processes files (CSV, JSON, PDF, images). Max 512MB per file.
```typescript
// Attach file to message
attachments: [{ file_id: "file_123", tools: [{ type: "code_interpreter" }] }]
// Access generated files
for (const content of message.content) {
if (content.type === 'image_file') {
const fileContent = await openai.files.content(content.image_file.file_id);
}
}
```
### File Search (RAG)
Semantic search with vector stores. **10,000 files max** (v2, was 20 in v1). **Pricing**: $0.10/GB/day (1GB free).
```typescript
// Create vector store
const vs = await openai.beta.vectorStores.create({ name: "Docs" });
await openai.beta.vectorStores.files.create(vs.id, { file_id: "file_123" });
// Wait for indexing
let store = await openai.beta.vectorStores.retrieve(vs.id);
while (store.status === 'in_progress') {
await new Promise(r => setTimeout(r, 2000));
store = await openai.beta.vectorStores.retrieve(vs.id);
}
// Use in assistant
tool_resources: { file_search: { vector_store_ids: [vs.id] } }
```
**⚠️ Wait for `status: 'completed'` before using**
### Function Calling
Submit tool outputs when run.status === 'requires_action':
```typescript
if (run.status === 'requires_action') {
const toolCalls = run.required_action.submit_tool_outputs.tool_calls;
const outputs = toolCalls.map(tc => ({
tool_call_id: tc.id,
output: JSON.stringify(yourFunction(JSON.parse(tc.function.arguments))),
}));
run = await openai.beta.threads.runs.submitToolOutputs(thread.id, run.id, {
tool_outputs: outputs,
});
}
```
## File Formats
**Code Interpreter**: .c, .cpp, .csv, .docx, .html, .java, .json, .md, .pdf, .php, .pptx, .py, .rb, .tex, .txt, .css, .jpeg, .jpg, .js, .gif, .png, .tar, .ts, .xlsx, .xml, .zip (512MB max)
**File Search**: .c, .cpp, .docx, .html, .java, .json, .md, .pdf, .php, .pptx, .py, .rb, .tex, .txt, .css, .js, .ts, .go (512MB max)
---
## Known Issues
**1. Thread Already Has Active Run**
```
Error: 400 Can't add messages to thread_xxx while a run run_xxx is active.
```
**Fix**: Cancel active run first: `await openai.beta.threads.runs.cancel(threadId, runId)`
**2. Run Polling Timeout / Incomplete Status**
```
Error: OpenAIError: Final run has not been received
```
**Why It Happens**: Long-running tasks may exceed polling windows or finish with `incomplete` status
**Prevention**: Handle incomplete runs gracefully
```typescript
try {
const stream = await openai.beta.threads.runs.stream(thread.id, { assistant_id });
for await (const event of stream) {
if (event.event === 'thread.message.delta') {
process.stdout.write(event.data.delta.content?.[0]?.text?.value || '');
}
}
} catch (error) {
if (error.message?.includes('Final run has not been received')) {
// Run ended with 'incomplete' status - thread can continue
const run = await openai.beta.threads.runs.retrieve(thread.id, runId);
if (run.status === 'incomplete') {
// Handle: prompt user to continue, reduce max_completion_tokens, etc.
}
}
}
```
**Source**: [GitHub Issues #945](https://github.com/openai/openai-node/issues/945), [#1306](https://github.com/openai/openai-node/issues/1306), [#1439](https://github.com/openai/openai-node/issues/1439)
**3. Vector Store Not Ready**
Using vector store before indexing completes.
**Fix**: Poll `vectorStores.retrieve()` until `status === 'completed'` (see File Search section)
**4. File Upload Format Issues**
Unsupported file formats cause silent failures.
**Fix**: Validate file extensions before upload (see File Formats section)
**5. Vector Store Upload Documentation Incorrect**
```
Error: No 'files' provided to process
```
**Why It Happens**: Official documentation shows incorrect usage of `uploadAndPoll`
**Prevention**: Wrap file streams in `{ files: [...] }` object
```typescript
// ✅ Correct
await openai.beta.vectorStores.fileBatches.uploadAndPoll(vectorStoreId, {
files: fileStreams
});
// ❌ Wrong (shown in official docs)
await openai.beta.vectorStores.fileBatches.uploadAndPoll(vectorStoreId, fileStreams);
```
**Source**: [GitHub Issue #1337](https://github.com/openai/openai-node/issues/1337)
**6. Reasoning Models Reject Temperature Parameter**
```
Error: Unsupported parameter: 'temperature' is not supported with this model
```
**Why It Happens**: When updating assistant to o3-mini/o1-preview/o1-mini, old temperature settings persist
**Prevention**: Explicitly set temperature to `null`
```typescript
await openai.beta.assistants.update(assistantId, {
model: 'o3-mini',
reasoning_effort: 'medium',
temperature: null, // ✅ Must explicitly clear
top_p: null
});
```
**Source**: [GitHub Issue #1318](https://github.com/openai/openai-node/issues/1318)
**7. uploadAndPoll Returns Vector Store ID Instead of Batch ID**
```
Error: Invalid 'batch_id': 'vs_...'. Expected an ID that begins with 'vsfb_'.
```
**Why It Happens**: `uploadAndPoll` returns vector store object instead of batch object
**Prevention**: Use alternative methods to get batch ID
```typescript
// Option 1: Use createAndPoll after separate upload
const batch = await openai.vectorStores.fileBatches.createAndPoll(
vectorStoreId,
{ file_ids: uploadedFileIds }
);
// Option 2: List batches to find correct ID
const batches = await openai.vectorStores.fileBatches.list(vectorStoreId);
const batchId = batches.data[0].id; // starts with 'vsfb_'
```
**Source**: [GitHub Issue #1700](https://github.com/openai/openai-node/issues/1700)
**8. Vector Store File Delete Affects All Stores**
**Warning**: Deleting a file from one vector store removes it from ALL vector stores
```typescript
// ❌ This deletes file from VS_A, VS_B, AND VS_C
await openai.vectorStores.files.delete('VS_A', 'file-xxx');
```
**Why It Happens**: SDK or API bug - delete operation has global effect
**Prevention**: Avoid sharing files across multiple vector stores if selective deletion is needed
**Source**: [GitHub Issue #1710](https://github.com/openai/openai-node/issues/1710)
**9. Memory Leak in Large File Uploads (Community-sourced)**
**Source**: [GitHub Issue #1052](https://github.com/openai/openai-node/issues/1052) | **Status**: OPEN
**Impact**: ~44MB leaked per 22MB file upload in long-running servers
**Why It Happens**: When uploading large files from streams (S3, etc.) using `vectorStores.fileBatches.uploadAndPoll`, memory may not be released after upload completes
**Verified**: Maintainer acknowledged, reduced in v4.58.1 but not eliminated
**Workaround**: Monitor memory usage in long-lived servers; restart periodically or use separate worker processes
**10. Thread Already Has Active Run - Race Condition (Community-sourced)**
**Enhancement to Issue #1**: When canceling an active run, race conditions may occur if the run completes before cancellation
```typescript
async function createRunSafely(threadId: string, assistantId: string) {
// Check for active runs first
const runs = await openai.beta.threads.runs.list(threadId, { limit: 1 });
const activeRun = runs.data.find(r =>
['queued', 'in_progress', 'requires_action'].includes(r.status)
);
if (activeRun) {
try {
await openai.beta.threads.runs.cancel(threadId, activeRun.id);
// Wait for cancellation to complete
let run = await openai.beta.threads.runs.retrieve(threadId, activeRun.id);
while (run.status === 'cancelling') {
await new Promise(r => setTimeout(r, 500));
run = await openai.beta.threads.runs.retrieve(threadId, activeRun.id);
}
} catch (error) {
// Ignore "already completed" errors - run finished naturally
if (!error.message?.includes('completed')) throw error;
}
}
return openai.beta.threads.runs.create(threadId, { assistant_id: assistantId });
}
```
**Source**: [OpenAI Community Forum](https://community.openai.com/t/error-running-thread-already-has-an-active-run/782118)
See `references/top-errors.md` for complete catalog.
## Relationship to Other Skills
**openai-api** (Chat Completions): Stateless, manual history, direct responses. Use for simple generation.
**openai-responses** (Responses API): ✅ **Recommended for new projects**. Better reasoning, modern MCP integration, active development.
**openai-assistants**: ⚠️ **Deprecated H1 2026**. Use for legacy apps only. Migration: `references/migration-to-responses.md`
---
## v1 to v2 Migration
**v1 deprecated**: Dec 18, 2024
**Key Changes**: `retrieval` → `file_search`, vector stores (10k files vs 20), 256k instructions (vs 32k), message-level file attachments
See `references/migration-from-v1.md`
---
**Templates**: `templates/basic-assistant.ts`, `code-interpreter-assistant.ts`, `file-search-assistant.ts`, `function-calling-assistant.ts`, `streaming-assistant.ts`
**References**: `references/top-errors.md`, `thread-lifecycle.md`, `vector-stores.md`, `migration-to-responses.md`, `migration-from-v1.md`
**Related Skills**: `openai-responses` (recommended), `openai-api`
---
**Last Updated**: 2026-01-21
**Package**: [email protected]
**Status**: Production Ready (⚠️ Deprecated - Sunset August 26, 2026)
**Changes**: Added 6 new known issues (vector store upload bugs, o3-mini temperature, memory leak), enhanced streaming error handling
This skill documents how to build and maintain stateful chatbots with the OpenAI Assistants API v2, including Code Interpreter, File Search (10k files), and Function Calling. It focuses on practical patterns, templates, and fixes for ten documented runtime and SDK issues. Note: the Assistants API is deprecated and sunsets August 26, 2026 — use openai-responses for new projects.
The content explains core objects (assistants, threads, messages, runs), async run patterns (create, poll, stream), tool integrations (code interpreter, file search with vector stores, function calling), and file handling. It includes templates and code snippets in TypeScript for creating assistants, managing threads, uploading files to vector stores, and handling run states and tool-required actions. The skill also catalogs known bugs with concrete workarounds and defensive code patterns.
Should I start new projects with this API?
No. The Assistants API v2 is deprecated; use openai-responses for new projects. Use this skill only for maintaining or migrating legacy systems within the deprecation window.
What causes silent file upload failures?
Unsupported file formats or incorrect upload payload shape. Validate extensions and wrap streams in { files: [...] } when calling uploadAndPoll to avoid the common doc mismatch.