can1357/oh-my-pi · error
Destination option ${key} must be a string
Error message
Destination option ${key} must be a string What it means
optionString reads a destination config option and throws when the value exists but is not a string. The blob-broker uploader runtime treats destination options as loosely typed user config (from JSON/TOML-like config files), so it validates each option's runtime type before use. Throwing a clear per-key message beats silently coercing a wrong-typed value like a number or nested object into a string.
Source
Thrown at packages/coding-agent/src/blob-broker/uploader-runtime.ts:56
constructor(destination: BlobDestinationId, reason: string) {
super(`${destination} is unavailable: ${reason}`);
this.name = "DestinationUnavailableError";
this.destination = destination;
}
}
/** Read a required option without coercing its configured type. */
export function requireOption(config: DestinationRuntimeConfig, key: string): DestinationOptionValue {
const value = config.options[key];
if (value === undefined) throw new Error(`Missing required destination option: ${key}`);
return value;
}
/** Read a string option, returning a fallback when it is absent. */
export function optionString(config: DestinationRuntimeConfig, key: string, fallback?: string): string | undefined {
const value = config.options[key];
if (value === undefined) return fallback;
if (typeof value !== "string") throw new Error(`Destination option ${key} must be a string`);
return value;
}
/** Read a number option, returning a fallback when it is absent. */
export function optionNumber(config: DestinationRuntimeConfig, key: string, fallback?: number): number | undefined {
const value = config.options[key];
if (value === undefined) return fallback;
if (typeof value !== "number" || !Number.isFinite(value)) {
throw new Error(`Destination option ${key} must be a finite number`);
}
return value;
}
/** Read a boolean option, returning a fallback when it is absent. */
export function optionBoolean(config: DestinationRuntimeConfig, key: string, fallback?: boolean): boolean | undefined {
const value = config.options[key];
if (value === undefined) return fallback;
if (typeof value !== "boolean") throw new Error(`Destination option ${key} must be a boolean`);View on GitHub (pinned to 9690622007)
Solutions
- Quote the value in the destination config so it parses as a string (e.g. `"port": "8080"`).
- Check the destination's expected option names/types in the uploader docs and fix the key's value type.
- If the value is genuinely numeric (like a port), confirm the destination uploader supports optionNumber and move it to the numeric option key.
Example fix
// before (config)
{ "options": { "uploadUrl": 8080 } }
// after
{ "options": { "uploadUrl": "https://example.com/upload" } } Defensive patterns
Strategy: validation
Validate before calling
function assertStringOption(options: Record<string, unknown>, key: string): void {
const v = options[key];
if (v !== undefined && typeof v !== "string") throw new Error(`option ${key} must be a string, got ${typeof v}`);
}
assertStringOption(config.options, "uploadUrl"); Type guard
const isString = (v: unknown): v is string => typeof v === "string";
Try / catch
try {
const url = optionString(config, "uploadUrl");
} catch (err) {
logger.warn("destination config invalid", { option: "uploadUrl", err: String(err) });
} Prevention
- Quote all string values in destination config files.
- Run config through a schema validator (zod/arktype) at load time before handing options to the runtime.
- Keep numeric values (ports, TTLs) in their dedicated numeric option keys, not string ones.
When it happens
Trigger: Calling optionString(config, key) — directly or via wrappers like server, configFile, tunnelName, ttl, uploadUrl, fileField — when config.options[key] is defined but typed as number, boolean, array, or object instead of string.
Common situations: Writing `port = 8080` (unquoted number) or `uploadUrl = true` in a destination config file; JSON config with a numeric value where a string URL is required; YAML/JSON5 parsers inferring types; copy-pasting config where quotes were lost.
Related errors
- Destination option ${key} must be a finite number
- Destination option ${key} must be a boolean
- Invalid pattern: {err}
- Failed to load tree-sitter language: {err}
- ${destination} returned an invalid upload URL
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/f481d37bcdb930bc.
Report an issue: GitHub.