{"record":{"id":"b8ddcdf9ede204c8","repo":"different-ai/openwork","slug":"projectdir-is-required","errorCode":null,"errorMessage":"projectDir is required","messagePattern":"projectDir is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/desktop/electron/main.mjs","lineNumber":1461,"sourceCode":"  }\n  const openworkServer = assertOpenworkServerReady(await runtimeManager.openworkServerInfo());\n  return { ok: true, skipped: false, engine, openworkServer, workspaceId: bootWorkspace.id ?? null };\n}\n\nfunction ensureRuntimeBootstrap() {\n  if (!runtimeBootstrapPromise) {\n    runtimeBootstrapPromise = bootRuntimeForSelectedWorkspace().catch((error) => ({\n      ok: false,\n      error: error instanceof Error ? error.message : String(error),\n    }));\n  }\n  return runtimeBootstrapPromise;\n}\n\nfunction resolveOpencodeConfigPath(scope, projectDir) {\n  if (scope === \"project\") {\n    if (!String(projectDir ?? \"\").trim()) {\n      throw new Error(\"projectDir is required\");\n    }\n    return workspaceOpencodeConfigCandidates(projectDir);\n  } else if (scope === \"global\") {\n    const root = globalOpencodeRoot();\n    return [path.join(root, \"opencode.jsonc\"), path.join(root, \"opencode.json\")];\n  } else {\n    throw new Error(\"scope must be 'project' or 'global'\");\n  }\n}\n\nasync function selectOpencodeConfigPath(candidates) {\n  for (const candidate of candidates) {\n    if (await pathExists(candidate)) return candidate;\n  }\n  return candidates[0];\n}\n\nasync function readOpencodeConfig(scope, projectDir) {","sourceCodeStart":1443,"sourceCodeEnd":1479,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/apps/desktop/electron/main.mjs#L1443-L1479","documentation":"resolveOpencodeConfigPath computes candidate opencode.json/opencode.jsonc paths for a given scope. For scope 'project', the candidates live under the project directory, so a non-empty projectDir is mandatory. When projectDir is missing, empty, or whitespace-only, the function throws 'projectDir is required' instead of guessing a directory.","triggerScenarios":"Any IPC/renderer call that resolves config paths with scope='project' (e.g. reading/editing project opencode config) while passing projectDir as undefined, null, empty string, or a whitespace-only string.","commonSituations":"Renderer invoked a config command before a workspace was selected, a workspace object lost its path field, or an API refactor renamed the parameter so the value no longer reaches projectDir.","solutions":["Pass a valid, non-empty absolute projectDir when calling with scope 'project'.","Ensure a workspace/project is selected in the UI before invoking project-scoped config operations.","Guard the caller: resolve the current workspace directory first and reject early if it is empty.","If you truly want user-level config, use scope 'global' instead, which needs no projectDir."],"exampleFix":"// before\nawait resolveOpencodeConfigPath(\"project\");\n// after\nawait resolveOpencodeConfigPath(\"project\", workspace.dir);","handlingStrategy":"validation","validationCode":"if (scope === 'project' && (typeof projectDir !== 'string' || projectDir.trim() === '')) {\n  throw new Error('select a workspace before reading project config');\n}","typeGuard":"function hasProjectDir(dir) {\n  return typeof dir === 'string' && dir.trim().length > 0;\n}","tryCatchPattern":null,"preventionTips":["Always resolve the active workspace path before project-scoped API calls.","Use scope 'global' when no project context is intended.","Type the scope/projectDir parameters in a shared IPC contract module.","Render project-config UI only after a workspace is open."],"tags":["validation","arguments","opencode-config"],"backgroundTag":"missing-required-argument","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}