can1357/oh-my-pi · error
imageUrls exposure "named-cloudflared" requires credentials.
Error message
imageUrls exposure "named-cloudflared" requires credentials.tunnelToken or options.configFile and options.tunnelName
What it means
A named Cloudflare Tunnel must be authenticated two ways: with a tunnel token, or with a config file plus the tunnel name. This error is thrown when neither credentials.tunnelToken nor the pair options.configFile + options.tunnelName is provided. It validates before spawning cloudflared so it fails fast instead of cloudflared exiting with its own cryptic error.
Source
Thrown at packages/coding-agent/src/blob-broker/exposure.ts:473
const argv = [binary, "local", String(port), "--to", server];
if (secret) argv.push("--secret", secret);
const { proc, baseUrl } = await spawnUrlTunnel(argv, line => parseBoreUrl(line, server));
return processExposure("bore", baseUrl, proc);
}
case "named-cloudflared": {
if (!config.publicBaseUrl) {
throw new Error('imageUrls exposure "named-cloudflared" requires imageUrls.publicBaseUrl');
}
const binary = requireBinary("cloudflared");
const token = credentialString(config, "tunnelToken");
let argv: string[];
if (token) {
argv = [binary, "tunnel", "--no-autoupdate", "run", "--token", token];
} else {
const configFile = optionString(config, "configFile");
const tunnelName = optionString(config, "tunnelName");
if (!configFile || !tunnelName) {
throw new Error(
'imageUrls exposure "named-cloudflared" requires credentials.tunnelToken or options.configFile and options.tunnelName',
);
}
argv = [binary, "tunnel", "--no-autoupdate", "--config", configFile, "run", tunnelName];
}
const baseUrl = normalizeBaseUrl(config.publicBaseUrl);
const { proc } = await spawnUrlTunnel(
argv,
() => baseUrl,
/Registered tunnel connection|Connection [a-z0-9-]+ registered/i,
);
return processExposure("named-cloudflared", baseUrl, proc);
}
case "ssh": {
if (!config.publicBaseUrl) throw new Error('imageUrls exposure "ssh" requires imageUrls.publicBaseUrl');
if (!config.sshTarget) throw new Error('imageUrls exposure "ssh" requires imageUrls.sshTarget');
const binary = requireBinary("ssh");
const remotePort = config.sshRemotePort ?? 8787;View on GitHub (pinned to 9690622007)
Solutions
- Set credentials.tunnelToken from `cloudflared tunnel token <tunnel-name>` — the simplest path.
- Or provide both options.configFile (path to cloudflared config.yml) and options.tunnelName.
- Verify the values are non-empty strings and under the right keys (credentials vs options).
- Run `cloudflared tunnel list` to confirm the tunnel exists.
Example fix
// before
"named-cloudflared": { "options": { "configFile": "/etc/cloudflared/config.yml" } }
// after
"named-cloudflared": { "options": { "configFile": "/etc/cloudflared/config.yml", "tunnelName": "omp-files" } } Defensive patterns
Strategy: validation
Validate before calling
if (config.kind === "named-cloudflared") {
const hasToken = typeof config.credentials?.tunnelToken === "string" && config.credentials.tunnelToken.length > 0;
const hasPair = typeof config.options?.configFile === "string" && config.options.configFile.length > 0 &&
typeof config.options?.tunnelName === "string" && config.options.tunnelName.length > 0;
if (!hasToken && !hasPair) throw new Error("named-cloudflared: set credentials.tunnelToken or options.configFile+tunnelName");
} Prevention
- Prefer credentials.tunnelToken — single value, no file path coupling
- Store the token via your secrets mechanism, never in plaintext logs
- If using configFile, verify the file exists and the tunnelName matches `cloudflared tunnel list`
- Validate the exposure config before starting the broker
When it happens
Trigger: kind "named-cloudflared" with publicBaseUrl set, but no credentials.tunnelToken and either options.configFile or options.tunnelName missing/empty.
Common situations: User created a tunnel in the Cloudflare dashboard but only copied the publicBaseUrl; config file path typo'd so optionString returns undefined; user supplied configFile but forgot tunnelName; token from `cloudflared tunnel token <name>` never pasted into credentials.
Related errors
- 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 $
- Unable to resolve AWS credentials. Configure static environm
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/2447142e2bd852df.
Report an issue: GitHub.