{"record":{"id":"5aa6b82c5afcd987","repo":"stablyai/orca","slug":"verify-packaged-plugin-resources-missing-launch","errorCode":null,"errorMessage":"[verify-packaged-plugin-resources] missing launch directory at ${launchRoot}","messagePattern":"\\[verify-packaged-plugin-resources\\] missing launch directory at (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"config/scripts/verify-packaged-plugin-resources.cjs","lineNumber":73,"sourceCode":"    hash.update(readFileSync(file.path))\n  }\n  return hash.digest('hex')\n}\n\nfunction readJsonFile(path, label) {\n  try {\n    return JSON.parse(readFileSync(path, 'utf8'))\n  } catch (error) {\n    throw new Error(\n      `[verify-packaged-plugin-resources] invalid ${label} at ${path}: ${error instanceof Error ? error.message : String(error)}`\n    )\n  }\n}\n\nfunction verifyPackagedPluginResources(resourcesDir) {\n  const launchRoot = join(resourcesDir, 'plugins', 'launch')\n  if (!statSync(launchRoot).isDirectory()) {\n    throw new Error(`[verify-packaged-plugin-resources] missing launch directory at ${launchRoot}`)\n  }\n  const index = readJsonFile(join(launchRoot, 'bundled-plugins.json'), 'bundled plugin index')\n  readJsonFile(join(launchRoot, 'orca-marketplace.json'), 'marketplace index')\n  if (index?.version !== 1 || !Array.isArray(index.plugins) || index.plugins.length === 0) {\n    throw new Error('[verify-packaged-plugin-resources] bundled plugin index is empty or invalid')\n  }\n  const resolvedRoot = resolve(launchRoot)\n  for (const entry of index.plugins) {\n    if (\n      typeof entry?.pluginKey !== 'string' ||\n      typeof entry.path !== 'string' ||\n      !/^[0-9a-f]{64}$/.test(entry.contentHash)\n    ) {\n      throw new Error('[verify-packaged-plugin-resources] bundled plugin entry is invalid')\n    }\n    const pluginRoot = resolve(launchRoot, entry.path)\n    const fromRoot = relative(resolvedRoot, pluginRoot)\n    if (!fromRoot || fromRoot === '..' || fromRoot.startsWith(`..${sep}`) || isAbsolute(fromRoot)) {","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/config/scripts/verify-packaged-plugin-resources.cjs#L55-L91","documentation":"Thrown by the packaged plugin resource verifier when statSync on the plugins/launch directory inside the Resources dir either fails (ENOENT — statSync throws, but the check is on .isDirectory() so a throw propagates) or the path exists but is not a directory. The launch directory is the root where all bundled plugins and their index files live; its absence means the plugin packaging step produced no output.","triggerScenarios":"verifyPackagedPluginResources(resourcesDir) is called on a packaged app where <resourcesDir>/plugins/launch does not exist. This means the electron-builder extraResources or files configuration that copies plugins into the package was not applied, or the source plugin directory was empty during packaging.","commonSituations":"The plugin build step was skipped or failed before packaging; electron-builder's extraResources config for plugins was removed or misconfigured; the plugins were written to a different directory than plugins/launch; the packaging ran from a clean checkout where plugins hadn't been built yet.","solutions":["Check if the directory exists in the packaged output: ls <resourcesDir>/plugins/launch/.","Verify the electron-builder extraResources or files configuration includes the plugins/launch directory.","Ensure the plugin build/bundling step runs before electron-builder packaging and outputs to the expected location.","Check the build logs for errors in the plugin packaging pipeline."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Before running verification, check the launch directory exists.\nconst { statSync } = require('node:fs')\nconst { join } = require('node:path')\n\nfunction preCheckLaunchDir(resourcesDir) {\n  const launchRoot = join(resourcesDir, 'plugins', 'launch')\n  try {\n    if (!statSync(launchRoot).isDirectory()) {\n      return { ok: false, message: `${launchRoot} exists but is not a directory` }\n    }\n    return { ok: true }\n  } catch {\n    return { ok: false, message: `${launchRoot} does not exist — plugin packaging step may have been skipped` }\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Ensure the plugin build and packaging step runs before the verification and before electron-builder packaging.","Document the expected directory layout (<resourcesDir>/plugins/launch/) in the build pipeline so missing directories are caught early.","Add a CI step that checks for the plugins/launch directory immediately after the build completes."],"tags":["plugin-packaging","ci-gate","resource-verification","directory-structure"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}