multica-ai/multica · error · Error

Invalid desktop runtime config: ${field} must use ws or wss

Error message

Invalid desktop runtime config: ${field} must use ws or wss

What it means

Validation error from hermesProfileDir: the canonicalized name is in hermesReservedProfileNames, so it cannot be used as a profile. Reserved names would collide with structural directories/files inside HERMES_HOME (the map is defined alongside the regex in hermes_home.go), hence they are rejected exactly like the Hermes CLI does.

Source

Thrown at apps/desktop/src/shared/runtime-config.ts:165

    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`);
  }
  url.search = "";
  url.hash = "";
  return trimTrailingSlash(url.toString());
}

function joinPath(base: string, suffix: string): string {
  const normalizedBase = base.endsWith("/") ? base.slice(0, -1) : base;
  return `${normalizedBase}${suffix}`;
}

function trimTrailingSlash(value: string): string {
  return value.replace(/\/+$/, "");
}

View on GitHub (pinned to 2c0912b6ec)

Solutions

  1. Pick a different, non-reserved profile name (check hermesReservedProfileNames in server/internal/daemon/execenv/hermes_home.go for the exact set).
  2. After upgrading the hermes CLI/daemon, review release notes for newly reserved names and rename affected profiles.
  3. Enforce the reserved list at agent-config validation time, not at task prepare time.

Example fix

// before
name := "profiles" // reserved
home, mustExist, err := hermesProfileDir(root, name) // err: reserved

// after
name := "team-profiles"
home, mustExist, err := hermesProfileDir(root, name)
Defensive patterns

Strategy: validation

Validate before calling

var hermesReservedProfileNames = map[string]bool{"profiles": true /* keep in sync with hermes_home.go */}
func usableHermesProfileName(name string) bool {
    n := strings.ToLower(strings.TrimSpace(name))
    return n != "" && n != "default" && !hermesReservedProfileNames[n]
}

Type guard

func isReservedHermesProfile(name string) bool {
    _, reserved := hermesReservedProfileNames[strings.ToLower(strings.TrimSpace(name))]
    return reserved
}

Try / catch

home, mustExist, err := hermesProfileDir(root, name)
if err != nil && strings.Contains(err.Error(), "is reserved") {
    // prompt for a rename; block reserved words at the entry point
}

Prevention

When it happens

Trigger: hermesProfileDir is called with a name that canonicalizes to one of the entries in hermesReservedProfileNames — the reserved set excludes names the overlay itself uses (anything colliding with root-level layout such as "profiles").

Common situations: A user names a profile "profiles" or another structural entry without realizing the CLI reserves it; after a hermes version upgrade adds new reserved names, previously valid configs start failing; shared default configs that used an now-reserved word.

Related errors


AI-assisted analysis of multica-ai/multica@2c0912b6ec (2026-08-15). Data as JSON: /api/errors/5fedd1551e03a03b. Report an issue: GitHub.