multica-ai/multica · error · Error
Invalid desktop runtime config: ${field} must be a valid URL
Error message
Invalid desktop runtime config: ${field} must be a valid URL What it means
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.
Source
Thrown at apps/desktop/src/shared/runtime-config.ts:147
throw new Error(`Invalid desktop runtime config: ${field} must be a non-empty string`);
}
return value;
}
function optionalString(value: unknown, field: string): string | undefined {
if (value === undefined) return undefined;
if (typeof value !== "string" || value.trim().length === 0) {
throw new Error(`Invalid desktop runtime config: ${field} must be a non-empty string when set`);
}
return value;
}
function normalizeHttpUrl(value: string, field: string): string {
let url: URL;
try {
url = new URL(value.trim());
} catch {
throw new Error(`Invalid desktop runtime config: ${field} must be a valid URL`);
}
if (url.protocol !== "http:" && url.protocol !== "https:") {
throw new Error(`Invalid desktop runtime config: ${field} must use http or https`);
}
url.search = "";
url.hash = "";
return trimTrailingSlash(url.toString());
}
function normalizeWsUrl(value: string, field: string): string {
let url: URL;
try {
url = new URL(value.trim());
} catch {
throw new Error(`Invalid desktop runtime config: ${field} must be a valid URL`);
}
if (url.protocol !== "ws:" && url.protocol !== "wss:") {
throw new Error(`Invalid desktop runtime config: ${field} must use ws or wss`);View on GitHub (pinned to 2c0912b6ec)
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.
Example fix
// before: env-root owned by previous daemon user → RemoveAll fails # ls -ld /var/lib/app/envs/task-123/codex-home/skills drwx------ 1 olduser olduser ... // after: hand the tree back to the daemon user # chown -R daemonuser:daemongroup /var/lib/app/envs/task-123
Defensive patterns
Strategy: validation
Validate before calling
skillsDir := filepath.Join(codexHome, "skills")
if fi, err := os.Lstat(skillsDir); err == nil && !fi.IsDir() {
return fmt.Errorf("codex skills path is not a directory: %s", skillsDir)
}
if err := checkWritable(codexHome); err != nil {
return fmt.Errorf("codex home not writable: %w", err)
} Try / catch
if err := hydrateCodexSkills(codexHome, ws, disabled, logger); err != nil {
if strings.Contains(err.Error(), "clear codex skills dir") {
// ownership/perms problem on the per-task CODEX_HOME: chown or wipe env-root, retry prepare
}
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Invalid desktop runtime config: expected a JSON object
- Unsupported desktop runtime config schemaVersion: expected 1
- Invalid desktop runtime config: ${field} must be a non-empty
- mint PAT: target API URL not set
- mint PAT: response missing token
AI-assisted analysis of multica-ai/multica@2c0912b6ec (2026-08-15).
Data as JSON: /api/errors/b5ad1c7dd4c41473.
Report an issue: GitHub.