{"record":{"id":"01af687e7ebaaf59","repo":"nexu-io/open-design","slug":"od-data-dir-is-required-when-od-sandbox-mode-is-en","errorCode":null,"errorMessage":"OD_DATA_DIR is required when OD_SANDBOX_MODE is enabled","messagePattern":"OD_DATA_DIR is required when OD_SANDBOX_MODE is enabled","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"apps/daemon/src/daemon-paths.ts","lineNumber":133,"sourceCode":"    resourceRoot ?? null,\n    path.join('data', 'plugin-previews'),\n    path.join(projectRoot, 'data', 'plugin-previews'),\n  );\n}\n\nexport interface ResolveDataDirOptions {\n  requireExplicit?: boolean;\n}\n\nexport function resolveDataDir(\n  raw: string | undefined,\n  projectRoot: string,\n  options: ResolveDataDirOptions = {},\n): string {\n  const value = raw?.trim();\n  if (!value) {\n    if (options.requireExplicit) {\n      throw new Error('OD_DATA_DIR is required when OD_SANDBOX_MODE is enabled');\n    }\n    return path.join(projectRoot, '.od');\n  }\n\n  const resolved = resolveProjectRelativePath(value, projectRoot);\n  try {\n    fs.mkdirSync(resolved, { recursive: true });\n    fs.accessSync(resolved, fs.constants.W_OK);\n  } catch (err) {\n    const e = err as Error;\n    const currentUser = (() => {\n      try {\n        return os.userInfo().username;\n      } catch {\n        return process.env.USER ?? process.env.LOGNAME ?? 'unknown';\n      }\n    })();\n    const parentDir = path.dirname(resolved);","sourceCodeStart":115,"sourceCodeEnd":151,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/daemon-paths.ts#L115-L151","documentation":"Thrown by resolveDataDir() when OD_DATA_DIR is empty/unset AND options.requireExplicit is true. requireExplicit is set when OD_SANDBOX_MODE is enabled: sandbox mode restricts the daemon to an explicit data root, so silently defaulting to <projectRoot>/.od would defeat the sandbox boundary. The message names exactly which env var combination is at fault.","triggerScenarios":"Starting the daemon with OD_SANDBOX_MODE enabled but OD_DATA_DIR not provided (or only whitespace). resolveDataDir is called with requireExplicit=true in that mode.","commonSituations":"A packaged/sandboxed deployment forgot to provision OD_DATA_DIR; sandbox mode was turned on for testing without also pointing OD_DATA_DIR at the sandbox volume; whitespace-only value passed env-var presence checks but trimmed to empty.","solutions":["Set OD_DATA_DIR to an explicit absolute path before starting the daemon in sandbox mode.","Disable OD_SANDBOX_MODE if sandbox isolation is not required (then the <projectRoot>/.od default applies).","Ensure the deployment manifest always pairs OD_SANDBOX_MODE with an OD_DATA_DIR volume."],"exampleFix":"# before: sandbox on, no data dir\nexport OD_SANDBOX_MODE=1\n# OD_DATA_DIR unset\n\n# after: pair sandbox with an explicit data dir\nexport OD_SANDBOX_MODE=1\nexport OD_DATA_DIR=/var/lib/open-design","handlingStrategy":"validation","validationCode":"if (process.env.OD_SANDBOX_MODE && !process.env.OD_DATA_DIR?.trim()) {\n  throw new Error('OD_DATA_DIR is required when OD_SANDBOX_MODE is enabled');\n}","typeGuard":"function sandboxConfigured(env: NodeJS.ProcessEnv): boolean {\n  return ['1','true','yes'].includes((env.OD_SANDBOX_MODE ?? '').trim().toLowerCase());\n}","tryCatchPattern":"try { resolveDataDir(process.env.OD_DATA_DIR, projectRoot, { requireExplicit: sandbox }); }\ncatch (e) {\n  if (e instanceof Error && /OD_DATA_DIR is required/.test(e.message)) {\n    // fail boot with a clear message: provision OD_DATA_DIR\n  } else throw e;\n}","preventionTips":["Always pair OD_SANDBOX_MODE with an OD_DATA_DIR volume in deployment manifests.","Add a preflight check in the launcher that asserts the pairing.","Document the sandbox contract wherever OD_SANDBOX_MODE is referenced."],"tags":["daemon","paths","sandbox","env-vars","boot"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}