{"record":{"id":"ad74506fccecd51e","repo":"different-ai/openwork","slug":"archive-is-missing-opencode-config","errorCode":null,"errorMessage":"Archive is missing .opencode config","messagePattern":"Archive is missing \\.opencode config","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/desktop/electron/workspace-archive.mjs","lineNumber":308,"sourceCode":"    if (!(await isDirectoryEmpty(targetDir))) throw new Error(\"Target folder must be empty\");\n  }\n  await mkdir(targetDir, { recursive: true });\n\n  const archiveStats = await stat(archivePath);\n  if (archiveStats.size > MAX_ARCHIVE_BYTES) throw new Error(\"Workspace archive is too large.\");\n  const buffer = await readFile(archivePath);\n  for (const entry of listZipEntries(buffer)) {\n    if (entry.name === \"manifest.json\" || entry.name.endsWith(\"/\")) continue;\n    if (!isSafeArchivePath(entry.name)) throw new Error(\"Archive contains an unsafe path\");\n    if (!(entry.name === \"opencode.json\" || entry.name.startsWith(\".opencode/\"))) continue;\n    if (isSecretName(path.basename(entry.name))) continue;\n    const outPath = path.join(targetDir, ...entry.name.split(\"/\"));\n    await mkdir(path.dirname(outPath), { recursive: true });\n    await writeFile(outPath, readZipEntryData(buffer, entry));\n  }\n\n  const opencodeDir = path.join(targetDir, \".opencode\");\n  if (!(await pathExists(opencodeDir))) throw new Error(\"Archive is missing .opencode config\");\n\n  const openworkPath = path.join(opencodeDir, \"openwork.json\");\n  let preset = \"starter\";\n  let workspaceName = typeof name === \"string\" && name.trim() ? name.trim() : null;\n\n  if (await pathExists(openworkPath)) {\n    const raw = await readFile(openworkPath, \"utf8\");\n    try {\n      const config = JSON.parse(raw);\n      config.authorizedRoots = [targetDir];\n      if (!workspaceName && typeof config.workspace?.name === \"string\" && config.workspace.name.trim()) {\n        workspaceName = config.workspace.name.trim();\n      }\n      if (typeof config.workspace?.preset === \"string\" && config.workspace.preset.trim()) {\n        preset = config.workspace.preset.trim();\n      }\n      await writeFile(openworkPath, `${JSON.stringify(config, null, 2)}\\n`, \"utf8\");\n    } catch {","sourceCodeStart":290,"sourceCodeEnd":326,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/apps/desktop/electron/workspace-archive.mjs#L290-L326","documentation":"After extracting all eligible entries from a workspace archive, importWorkspaceConfig verifies that a .opencode directory exists under targetDir. An archive can be a structurally valid ZIP yet contain no .opencode/ files (or only opencode.json), meaning it would not yield a usable OpenWork workspace config, so the import aborts.","triggerScenarios":"Calling importWorkspaceConfig with an archive that contains no files under '.opencode/' — e.g. only manifest.json and opencode.json, or an archive of the wrong folder entirely.","commonSituations":"Hand-built archives zipped from the wrong directory; archives where .opencode entries were skipped because names matched secret-name filters; zipping the parent folder so entries are 'myproject/.opencode/...' with no top-level .opencode files.","solutions":["Repack the archive so it contains .opencode/ entries at the archive root (at least one non-secret file under .opencode/).","Re-export with exportWorkspaceConfig from an existing workspace instead of zipping manually.","Verify you are importing the right archive and that the zip root is the workspace folder, not its parent."],"exampleFix":"// before: zip -r workspace.zip ./my-project  -> entries 'my-project/.opencode/...'\n// after: run from inside the workspace root\nzip -r workspace.zip opencode.json .opencode/","handlingStrategy":"validation","validationCode":"import { readFile } from \"node:fs/promises\";\nimport { listZipEntries } from \"./workspace-archive.mjs\";\nasync function archiveHasOpencodeConfig(archivePath) {\n  const buffer = await readFile(archivePath);\n  return listZipEntries(buffer).some(\n    (e) => e.name === \"opencode.json\" || e.name.startsWith(\".opencode/\"),\n  );\n}","typeGuard":null,"tryCatchPattern":"try {\n  await importWorkspaceConfig({ archivePath, targetDir });\n} catch (err) {\n  if (err.message === \"Archive is missing .opencode config\") {\n    // prompt user for a valid workspace archive\n  } else throw err;\n}","preventionTips":["Zip from inside the workspace root so .opencode/ sits at the archive root.","Verify the archive lists a .opencode/ entry before importing.","Prefer exportWorkspaceConfig over manual zipping."],"tags":["validation","zip","missing-config","import"],"backgroundTag":"missing-required-config","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}