{"record":{"id":"b5ad1c7dd4c41473","repo":"multica-ai/multica","slug":"invalid-desktop-runtime-config-field-must-be-a-b5ad1c","errorCode":null,"errorMessage":"Invalid desktop runtime config: ${field} must be a valid URL","messagePattern":"Invalid desktop runtime config: (.+?) must be a valid URL","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/desktop/src/shared/runtime-config.ts","lineNumber":147,"sourceCode":"    throw new Error(`Invalid desktop runtime config: ${field} must be a non-empty string`);\n  }\n  return value;\n}\n\nfunction optionalString(value: unknown, field: string): string | undefined {\n  if (value === undefined) return undefined;\n  if (typeof value !== \"string\" || value.trim().length === 0) {\n    throw new Error(`Invalid desktop runtime config: ${field} must be a non-empty string when set`);\n  }\n  return value;\n}\n\nfunction normalizeHttpUrl(value: string, field: string): string {\n  let url: URL;\n  try {\n    url = new URL(value.trim());\n  } catch {\n    throw new Error(`Invalid desktop runtime config: ${field} must be a valid URL`);\n  }\n  if (url.protocol !== \"http:\" && url.protocol !== \"https:\") {\n    throw new Error(`Invalid desktop runtime config: ${field} must use http or https`);\n  }\n  url.search = \"\";\n  url.hash = \"\";\n  return trimTrailingSlash(url.toString());\n}\n\nfunction normalizeWsUrl(value: string, field: string): string {\n  let url: URL;\n  try {\n    url = new URL(value.trim());\n  } catch {\n    throw new Error(`Invalid desktop runtime config: ${field} must be a valid URL`);\n  }\n  if (url.protocol !== \"ws:\" && url.protocol !== \"wss:\") {\n    throw new Error(`Invalid desktop runtime config: ${field} must use ws or wss`);","sourceCodeStart":129,"sourceCodeEnd":165,"githubUrl":"https://github.com/multica-ai/multica/blob/2c0912b6ec764b373d44eeea1e80f0d9f11ab417/apps/desktop/src/shared/runtime-config.ts#L129-L165","documentation":"Returned by hydrateCodexSkills when os.RemoveAll of <codexHome>/skills fails. Codex is the only runtime needing two-stage hydration because the daemon sets CODEX_HOME to a per-task directory; the skills dir must be cleared first so removed user skills and stale workspace skills from a previous run do not remain visible to the codex CLI.","triggerScenarios":"hydrateCodexSkills(codexHome, ...) is called and os.RemoveAll(filepath.Join(codexHome, \"skills\")) errors — the per-task CODEX_HOME (or a file inside its skills dir) cannot be removed: permission mismatch, read-only mount, an undeletable file held open, or a path that is not a directory tree the daemon user owns.","commonSituations":"Per-task codex home created by a different user (daemon restarted under a new UID); skills dir on a read-only or NFS mount with stale handles; a file under skills/ with no write permission on its parent; SELinux/AppArmor denying unlink.","solutions":["Check the wrapped *PathError for the exact path, then inspect its ownership and permissions; chown/chmod it to the daemon user.","If the env-root is recycled between runs, ensure the same daemon user owns it, or clear stale per-task dirs before re-preparing.","Remount read-only filesystems read-write, or move the env-root to writable storage.","Remove undeletable held-open files (lsof on the path) or reboot stale NFS handles, then retry."],"exampleFix":"// before: env-root owned by previous daemon user → RemoveAll fails\n# ls -ld /var/lib/app/envs/task-123/codex-home/skills\ndrwx------ 1 olduser olduser ...\n\n// after: hand the tree back to the daemon user\n# chown -R daemonuser:daemongroup /var/lib/app/envs/task-123","handlingStrategy":"validation","validationCode":"skillsDir := filepath.Join(codexHome, \"skills\")\nif fi, err := os.Lstat(skillsDir); err == nil && !fi.IsDir() {\n    return fmt.Errorf(\"codex skills path is not a directory: %s\", skillsDir)\n}\nif err := checkWritable(codexHome); err != nil {\n    return fmt.Errorf(\"codex home not writable: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"if err := hydrateCodexSkills(codexHome, ws, disabled, logger); err != nil {\n    if strings.Contains(err.Error(), \"clear codex skills dir\") {\n        // ownership/perms problem on the per-task CODEX_HOME: chown or wipe env-root, retry prepare\n    }\n}","preventionTips":["Run the daemon under a stable UID that owns all env-roots.","Purge per-task codex homes at task end so hydration always starts clean.","Avoid read-only/NFS mounts for per-task env-roots."],"tags":["go","execenv","codex","filesystem","skills"],"backgroundTag":null,"analyzedSha":"2c0912b6ec764b373d44eeea1e80f0d9f11ab417","analyzedAt":"2026-08-15T13:25:18.241Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}