[BUG] It might be a bug on v0-sdk/V0 API

I created two scripts that do the same thing but in different ways. The first one uses jszip to extract a ZIP and sends each file to V0 by using the v0.chats.updateVersion() function, and in the end sends a message. The second one uses v0.chats.init() with a ZIP URL as a parameter, then sends a message.

Both of these scripts has the same goal:

  1. init() a chat
  2. upload a template project
  3. send a message

Both scripts are using init(). (shit code, both scripts is just to show the behavior)

// v0-init.ts

import 'dotenv/config';
import { createClient } from 'v0-sdk';
import JSZip from 'jszip';

const isTextFile = (filename: string) => {
	const textExtensions = [
		'.ts',
		'.tsx',
		'.js',
		'.jsx',
		'.json',
		'.css',
		'.scss',
		'.html',
		'.md',
		'.txt',
		'.svg',
		'.yaml',
		'.yml',
		'.toml',
		'.env',
		'.gitignore',
		'.eslintrc',
		'.prettierrc',
		'.mjs',
		'.tsbuildinfo'
	];
	const basename = filename.split('/').pop() ?? '';
	return (
		textExtensions.some((ext) => filename.endsWith(ext)) ||
		basename.startsWith('.') ||
		!basename.includes('.')
	);
};

const main = async () => {
	const apiKey = process.env.V0_API_KEY;
	const v0 = createClient({ apiKey });

	const initResult = await v0.chats.init({
		name: 'init test with no files',
		type: 'files',
		files: [{ name: '.gitkeep', content: '' }]
	});

	const chatId = initResult.id;
	const versionId = initResult.latestVersion?.id;
	if (!versionId) {
		throw new Error(`Init returned chat=${chatId} without latestVersion.id`);
	}

	const response = await fetch(
		'.../heads/main.zip'
	);
	if (!response.ok) {
		throw new Error(`Failed to fetch zip: ${response.status}`);
	}
	const zipBuffer = await response.arrayBuffer();

	const zip = await JSZip.loadAsync(zipBuffer);

	const files: { name: string; content: string }[] = [];

	const entries = Object.keys(zip.files);
	const rootFolder = (entries[0]?.split('/')[0] ?? '') + '/';

	for (const [path, zipEntry] of Object.entries(zip.files)) {
		if (zipEntry.dir) continue;

		const relativePath = path.startsWith(rootFolder) ? path.slice(rootFolder.length) : path;
		if (!relativePath) continue;

		if (!isTextFile(relativePath)) {
			console.log(`Skipping binary file: ${relativePath}`);
			continue;
		}

		const content = await zipEntry.async('string');
		files.push({ name: relativePath, content });
	}

	await v0.chats.updateVersion({
		chatId,
		versionId,
		files
	});

	await v0.chats.sendMessage({
		chatId,
		message: 'make it darkmode only'
	});

	console.log('uwu done!');
	console.log(`chatUrl=https://v0.app/chat/${chatId}`);
};

main().catch((err) => {
	console.error(err instanceof Error ? err.message : err);
	process.exit(1);
});
// v0-init-zip.ts

import 'dotenv/config';
import { createClient } from 'v0-sdk';

const main = async () => {
	const apiKey = process.env.V0_API_KEY;
	if (!apiKey) {
		throw new Error('V0_API_KEY is missing from environment.');
	}

	const v0 = createClient({ apiKey });

	const initResult = await v0.chats.init({
		name: 'init test from zip',
		type: 'zip',
		zip: {
			url: '.../heads/main.zip'
		}
	});

	const chatId = initResult.id;
	const versionId = initResult.latestVersion?.id;
	if (!versionId) {
		throw new Error(`Init returned chat=${chatId} without latestVersion.id`);
	}

	await v0.chats.sendMessage({
		chatId,
		message: 'make it darkmode only'
	});

	console.log('uwu done!');
	console.log(`chatUrl=https://v0.app/chat/${chatId}`);
};

main().catch((err) => {
	console.error(err instanceof Error ? err.message : err);
	process.exit(1);
});

Both script runs, but the one using init() with the ZIP file as a parameter, created a chat with no file:

While the one doing the workaround by extracting the ZIP, using the updateVersion etc., did everything as expected:

Also, the version with no files, when it runs the init(), it creates a version, but this version remains on “pending” forever.

Please let me know if I’m doing something wrong!

Hi @oliver-8419, thanks for reaching out. I’m the community’s system bot.

Sorry to hear you’re experiencing this issue with the v0 SDK! The detailed code examples and screenshots are really helpful for understanding what’s happening.

To help investigate this further, could you share the v0 chat links for both scenarios? They would look something like https://v0.dev/chat/... - especially the one where the ZIP initialization isn’t working properly and shows no files.

Also, are you seeing any error messages in your console output when running the second script (the one that uses type: 'zip'), or does it complete successfully despite not loading the files?