can1357/oh-my-pi · error · Error
Destination option ${key} must be a non-empty string
Error message
Destination option ${key} must be a non-empty string What it means
Thrown by requiredStringOption when a destination config option (fetched via requireOption) is present but is not a string, or is a string that is empty/whitespace-only. Self-hosted uploaders call it for every mandatory option (host, publicBase, root, apiUrl, repositoryId, base), so a bad option value fails fast at uploader creation.
Source
Thrown at packages/coding-agent/src/blob-broker/uploaders-self-hosted.ts:87
}
function plikUpload(value: unknown): PlikUpload {
if (typeof value !== "object" || value === null || !("id" in value) || !("uploadToken" in value)) {
throw new Error("plik upload metadata did not include an id and upload token");
}
const id = identifier(value.id);
const uploadToken = nonEmptyString(value.uploadToken);
if (!id || !uploadToken) throw new Error("plik upload metadata did not include an id and upload token");
let downloadBase: string | undefined;
if ("downloadURL" in value) downloadBase = nonEmptyString(value.downloadURL);
if (!downloadBase && "downloadDomain" in value) downloadBase = nonEmptyString(value.downloadDomain);
return { id, uploadToken, ...(downloadBase ? { downloadBase } : {}) };
}
function requiredStringOption(config: DestinationRuntimeConfig, key: string): string {
const value = requireOption(config, key);
if (typeof value !== "string" || value.trim() === "") {
throw new Error(`Destination option ${key} must be a non-empty string`);
}
return value.trim();
}
function pathParts(value: string | undefined): string[] {
if (!value) return [];
const parts = value.replaceAll("\\", "/").split("/");
const result: string[] = [];
for (const part of parts) {
if (!part || part === ".") continue;
if (part === ".." || part.includes("\0"))
throw new Error("Destination paths cannot contain parent traversal or NUL bytes");
result.push(part);
}
return result;
}
function safeFileName(request: BlobUploadRequest): string {View on GitHub (pinned to 9690622007)
Solutions
- Open the destination config and set the named option (shown in the error message) to a real non-empty string
- Check where the value comes from — an unset env variable interpolating to "" is the usual culprit
- Quote string values in YAML/JSON so numbers/booleans don't leak in (e.g. port: "22" not port: 22)
- Validate the full destination config before saving it (run each required option through typeof v === 'string' && v.trim() !== '')
Example fix
// before
{ "type": "webdav", "host": "", "root": 42 }
// after
{ "type": "webdav", "host": "cloud.example.com", "root": "/remote.php/webdav" } Defensive patterns
Strategy: validation
Validate before calling
function requireNonEmptyString(config: Record<string, unknown>, key: string): string {
const v = config[key];
if (typeof v !== "string" || v.trim() === "") throw new Error(`Destination option ${key} must be a non-empty string (got ${JSON.stringify(v)})`);
return v.trim();
}
// validate all required keys before creating the destination
for (const k of ["host", "root", "publicBase"]) requireNonEmptyString(destConfig, k); Type guard
function isFilledString(value: unknown): value is string {
return typeof value === "string" && value.trim().length > 0;
} Try / catch
try {
const uploader = createWebdavUploader(dest);
} catch (err) {
const m = (err as Error).message.match(/Destination option (\S+) must be a non-empty string/);
if (m) throw new Error(`Fix your destination config: option '${m[1]}' is missing or empty`);
throw err;
} Prevention
- Fill every required option in the destination template before saving
- Check env vars used in config interpolation are actually set (empty HOST= yields "")
- Quote values so YAML/JSON don't coerce them to numbers/booleans
- Validate the config with a schema (zod/arktype) at load time
When it happens
Trigger: Creating any self-hosted blob destination (webdav, ftp, sftp, gitea, etc.) where a required option is set to an empty string, a number, boolean, null, or whitespace — e.g. `"host": ""` or `"apiUrl": 8080` in the destination config.
Common situations: Empty env-var interpolation in config (HOST= empty), YAML/JSON type coercion turning a value into a number, copy-pasting a config template without filling fields, or trimming leaving only whitespace.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- Destination option ${optionName} must be an absolute HTTP UR
- OMP_AUTH_BROKER_ACCOUNT_POOL_FILE contains an empty provider
- OMP_AUTH_BROKER_ACCOUNT_POOL_FILE contains a provider id wit
- ${name} path does not exist: ${trimmed}
- Anthropic thinking budget requires max_tokens greater than $
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/7a4a693a98a16f95.
Report an issue: GitHub.