usebruno/bruno · error · Error
Invalid collection: Neither bruno.json nor opencollection.ym
Error message
Invalid collection: Neither bruno.json nor opencollection.yml found in the ZIP file
What it means
Thrown during Bruno collection ZIP import. After extraction the importer picks the collection directory (the temp dir, or the single subdirectory when the archive wraps everything in one folder) and requires a `bruno.json` or `opencollection.yml` at its root. If neither marker file is present the ZIP is rejected as not a valid Bruno collection.
Source
Thrown at packages/bruno-electron/src/ipc/collection.js:2723
validateNoExternalSymlinks(tempDir, tempDir);
const extractedItems = fs.readdirSync(tempDir);
let collectionDir = tempDir;
if (extractedItems.length === 1) {
const singleItem = path.join(tempDir, extractedItems[0]);
const singleItemStat = fs.lstatSync(singleItem);
if (singleItemStat.isDirectory() && !singleItemStat.isSymbolicLink()) {
collectionDir = singleItem;
}
}
const brunoJsonPath = path.join(collectionDir, 'bruno.json');
const openCollectionYmlPath = path.join(collectionDir, 'opencollection.yml');
if (!fs.existsSync(brunoJsonPath) && !fs.existsSync(openCollectionYmlPath)) {
throw new Error('Invalid collection: Neither bruno.json nor opencollection.yml found in the ZIP file');
}
// Ensure config files are not symlinks
if (fs.existsSync(brunoJsonPath) && fs.lstatSync(brunoJsonPath).isSymbolicLink()) {
throw new Error('Security error: bruno.json cannot be a symbolic link');
}
if (fs.existsSync(openCollectionYmlPath) && fs.lstatSync(openCollectionYmlPath).isSymbolicLink()) {
throw new Error('Security error: opencollection.yml cannot be a symbolic link');
}
let collectionName = 'Imported Collection';
let brunoConfig = { name: collectionName, version: '1', type: 'collection', ignore: ['node_modules', '.git'] };
if (fs.existsSync(openCollectionYmlPath)) {
try {
const content = fs.readFileSync(openCollectionYmlPath, 'utf8');
const parsed = parseCollection(content, { format: 'yml' });
brunoConfig = parsed.brunoConfig || brunoConfig;
collectionName = brunoConfig.name || collectionName;View on GitHub (pinned to 9bdd81c7bd)
Solutions
- Open the ZIP and confirm bruno.json (or opencollection.yml) is at the archive root or inside exactly one top-level folder.
- Re-create the archive from inside the collection directory (e.g. `zip -r coll.zip .`) so the config sits at the right level.
- If importing from another tool, run it through Bruno's import/conversion flow before zipping.
- Verify the marker file name and extension exactly: `bruno.json` / `opencollection.yml`.
Example fix
// before: archive layout
my-collection.zip
nested-folder/
my-collection/ <- bruno.json lives here (too deep)
bruno.json
get.bru
// after: config at most one folder below root
my-collection.zip
my-collection/
bruno.json
get.bru Defensive patterns
Strategy: validation
Validate before calling
const isValidCollectionZip = (zipPath) => {
const AdmZip = require('adm-zip');
const zip = new AdmZip(zipPath);
const entries = zip.getEntries().map(e => e.entryName);
return entries.some(e => /^([^/]+\/)?(bruno\.json|opencollection\.yml)$/.test(e));
}; Prevention
- Always export collections via Bruno's own export rather than hand-zipping.
- Before import, open the archive and confirm the marker config file's depth.
- Keep collection directories flat - config at the collection root.
When it happens
Trigger: Importing a ZIP whose `bruno.json`/`opencollection.yml` sits more than one directory below the archive root (the single-item unwrap only descends one level); a ZIP that drops request files at the root with no config; importing an archive exported from another tool (Postman/Insomnia) without conversion; the marker file was renamed or is itself inside a second nested folder.
Common situations: User re-zips a folder incorrectly (zips the parent so an extra wrapper appears, or nests the collection one level too deep); editing an exported archive and breaking its layout; importing a non-Bruno archive directly.
Related errors
- The Collection file is corrupted
- ZIP file does not exist
- Collection location does not exist
- Import collection failed
- Invalid environment: expected an object
AI-assisted analysis of usebruno/bruno@9bdd81c7bd (2026-08-13).
Data as JSON: /api/errors/0a7797feabfb2983.
Report an issue: GitHub.