can1357/oh-my-pi · error · DestinationUnavailableError

no built-in uploader or serving adapter is implemented

Error message

no built-in uploader or serving adapter is implemented

What it means

The blob broker's createConfiguredUploader() resolves a destination name (e.g. 'r2', 's3') to an uploader implementation. Only certain destinations have built-in adapters; anything else that is not local-serving or tunnel (which return null, meaning handled elsewhere) has no implementation, so the broker throws DestinationUnavailableError. It is a deliberate 'not implemented yet' guard, not a configuration typo check.

Source

Thrown at packages/coding-agent/src/blob-broker/uploaders.ts:161

		}
		return createCommandUploader(command);
	}

	const uploader =
		createAnonymousUploader(destination, config) ??
		createImageHostUploader(destination, config) ??
		createCloudDriveUploader(destination, config) ??
		createObjectStorageUploader(destination, config) ??
		createSelfHostedUploader(destination, config) ??
		createLegacyUploader(destination, config) ??
		createDiscordUploader(destination, config);
	if (uploader) return uploader;

	if (destination === "provider-files") {
		throw new DestinationUnavailableError(destination, "provider-native files must use the provider file channel");
	}
	if (metadata.family === "local-serving" || metadata.family === "tunnel") return null;
	throw new DestinationUnavailableError(destination, "no built-in uploader or serving adapter is implemented");
}

/** Wrap an uploader with per-hash memoization so bytes upload at most once. */
export function memoizeUploader(
	uploader: BlobUploader,
): (hash: string, request: BlobUploadRequest) => Promise<BlobPublication | null> {
	const byHash = new Map<string, Promise<BlobPublication | null>>();
	return (hash, request) => {
		let pending = byHash.get(hash);
		if (!pending) {
			pending = uploader.upload(request).catch(error => {
				byHash.delete(hash);
				logger.warn("blob-broker: upload failed; image stays inline", {
					uploader: uploader.destination,
					error: error instanceof Error ? error.message : String(error),
				});
				return null;
			});

View on GitHub (pinned to 9690622007)

Solutions

  1. Check the destination name in config and switch to a supported destination (provider-files, local-serving, or tunnel)
  2. Register a custom uploader before calling createConfiguredUploader so the memoized `uploader` short-circuit returns it instead of falling through to the throw
  3. If you own the code, implement an uploader adapter for the destination family in packages/coding-agent/src/blob-broker/uploaders.ts
  4. If you only need local serving, use a local-serving family destination, which returns null instead of throwing

Example fix

// before
const up = await createConfiguredUploader({ destination: "gcs", metadata });
// after
const up = await createConfiguredUploader({ destination: "local-serving", metadata }); // or pass a pre-built uploader
Defensive patterns

Strategy: validation

Validate before calling

const SUPPORTED = new Set(["provider-files", "local-serving", "tunnel"]);
if (!SUPPORTED.has(destination) && !customUploader) {
  throw new Error(`destination "${destination}" has no built-in uploader; provide one or pick a supported destination`);
}

Type guard

function hasBuiltInAdapter(destination: string): boolean {
  return ["provider-files", "local-serving", "tunnel"].includes(destination);
}

Try / catch

try {
  uploader = await createConfiguredUploader({ destination, metadata });
} catch (err) {
  if (err instanceof DestinationUnavailableError) {
    logger.warn("destination unsupported, skipping upload", { destination });
    return null;
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling createConfiguredUploader (via uploader() or configChecks()) with a destination string that has a registered metadata family but no built-in uploader adapter — i.e. any non-'provider-files', non-'local-serving', non-'tunnel' destination whose uploader factory was never implemented.

Common situations: A user configures blob uploads to a custom/exotic destination in settings expecting support; a plugin author adds a destination family without wiring an uploader; a new destination type was added to metadata before the adapter shipped.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/87ed8cb17076a3b9. Report an issue: GitHub.