can1357/oh-my-pi · error · Error
Invalid global daemon service name: ${JSON.stringify(service
Error message
Invalid global daemon service name: ${JSON.stringify(service)} What it means
getGlobalDaemonRuntimeDir(service) builds a machine-global runtime directory path by joining the service name under the daemon runtime root. Because the name becomes a filesystem path segment, it is validated against /^[a-z0-9][a-z0-9._-]*$/i; any name containing path separators, leading dots, spaces, or other special characters throws this error to prevent path traversal or malformed runtime paths.
Source
Thrown at packages/utils/src/dirs.ts:971
export function getDaemonRuntimeRoot(): string {
return dirs.rootSubdir(path.join("run", "daemons"), "state");
}
/** Get the daemon runtime directory for a project (~/.omp/run/daemons/<hash>; XDG default: $XDG_STATE_HOME/omp/run/daemons/<hash>). */
export function getDaemonRuntimeDir(projectDir: string): string {
const key = Bun.hash.wyhash(path.resolve(projectDir)).toString(16).padStart(16, "0");
return path.join(getDaemonRuntimeRoot(), key);
}
/** Root directory containing every machine-global daemon service scope. */
export function getGlobalDaemonRuntimeRoot(): string {
return path.join(getBaseConfigRoot(), "run", "daemons", "global");
}
/** Get a profile-independent runtime directory for a machine-global daemon service. */
export function getGlobalDaemonRuntimeDir(service: string): string {
if (!/^[a-z0-9][a-z0-9._-]*$/i.test(service)) {
throw new Error(`Invalid global daemon service name: ${JSON.stringify(service)}`);
}
return path.join(getGlobalDaemonRuntimeRoot(), service);
}
/** Get the provider in-flight root directory (~/.omp/run/provider-inflight; XDG default: $XDG_STATE_HOME/omp/run/provider-inflight). */
export function getProviderInFlightRoot(): string {
return dirs.rootSubdir(path.join("run", "provider-inflight"), "state");
}
/** Get the marketplaces registry path (~/.omp/marketplaces.json; XDG default: $XDG_DATA_HOME/omp/marketplaces.json). Adopts a legacy registry on first XDG resolution. */
export function getMarketplacesRegistryPath(): string {
const registryPath = dirs.rootSubdir("marketplaces.json", "data");
adoptLegacyFile(path.join(dirs.configRoot, "marketplaces.json"), registryPath);
return registryPath;
}
// =============================================================================
// Project subdirectories (.omp/*)View on GitHub (pinned to 9690622007)
Solutions
- Sanitize the service name before calling: strip or replace illegal characters, e.g. name.replace(/[^a-zA-Z0-9._-]/g, "-").
- Ensure the name starts with an alphanumeric character (prepend one or trim leading dots/dashes).
- Derive the service name from a fixed, known identifier (package/tool name) rather than free-form user input.
- Validate with the same regex the library uses ( /^[a-z0-9][a-z0-9._-]*$/i ) at your config-loading boundary and reject invalid config early.
Example fix
// before
const dir = getGlobalDaemonRuntimeDir(`${org}/${tool}`); // "acme/preview" throws
// after
const service = `${org}-${tool}`.replace(/[^a-zA-Z0-9._-]/g, "-").replace(/^[^a-zA-Z0-9]+/, "");
const dir = getGlobalDaemonRuntimeDir(service); Defensive patterns
Strategy: validation
Validate before calling
const SERVICE_RE = /^[a-z0-9][a-z0-9._-]*$/i;
function isValidDaemonService(name: string): boolean {
return SERVICE_RE.test(name);
}
if (!isValidDaemonService(service)) throw new Error(`Refusing invalid daemon service name: ${service}`); Type guard
function isValidDaemonService(name: string): boolean {
return /^[a-z0-9][a-z0-9._-]*$/i.test(name);
} Try / catch
let dir: string;
try {
dir = getGlobalDaemonRuntimeDir(service);
} catch (err) {
if (err instanceof Error && err.message.startsWith("Invalid global daemon service name")) {
dir = getGlobalDaemonRuntimeDir(sanitizeServiceName(service));
} else throw err;
} Prevention
- Sanitize user/config-derived names with .replace(/[^a-zA-Z0-9._-]/g, "-") before use.
- Never pass strings containing "/", "\\", ":", or spaces as path segments.
- Validate identifiers at the config-parsing boundary, not at the call site.
- Prefer fixed, tool-owned names over free-form user input for global services.
When it happens
Trigger: Calling getGlobalDaemonRuntimeDir() with a service string that is empty, starts with a dot or non-alphanumeric character, or contains characters outside [a-z0-9._-] — e.g. "my daemon", "svc/v2", "../escape", "-svc", "".
Common situations: Passing a user-supplied profile/project name straight through as the daemon service name; building the service id by concatenating strings with slashes or colons (e.g. "org:tool"); interpolating an env value or config key that contains spaces or dots at the start.
Related errors
- Shared-folder destination escapes its configured root
- invalid job name: ${jobName}
- Managed skill "${name}" SKILL.md is not a regular file; refu
- Destination paths cannot contain parent traversal or NUL byt
- Path is not a file: ${pathArg}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/72a0bbbf714bd784.
Report an issue: GitHub.