jackwener/OpenCLI · error · ArgumentError

Unsupported remote image format "${normalizedContentType ||

Error message

Unsupported remote image format "${normalizedContentType || 'unknown'}". Supported: jpg, jpeg, png, gif, webp

What it means

resolveImageExtension determines the file extension for a downloaded remote image from its Content-Type header, falling back to the URL pathname extension. If neither maps to a supported type (jpg/jpeg/png/gif/webp) it throws ArgumentError. This ensures the file saved for the composer upload has an extension X accepts.

Source

Thrown at clis/twitter/utils.js:73

/**
 * Resolve the file extension to use when persisting a remote image: prefer
 * Content-Type, fall back to URL pathname.
 */
export function resolveImageExtension(url, contentType) {
    const normalizedContentType = (contentType || '').split(';')[0].trim().toLowerCase();
    if (normalizedContentType && CONTENT_TYPE_TO_EXTENSION[normalizedContentType]) {
        return CONTENT_TYPE_TO_EXTENSION[normalizedContentType];
    }
    try {
        const pathname = new URL(url).pathname;
        const ext = path.extname(pathname).toLowerCase();
        if (SUPPORTED_IMAGE_EXTENSIONS.has(ext))
            return ext;
    } catch {
        // Fall through to the final error below.
    }
    throw new ArgumentError(
        `Unsupported remote image format "${normalizedContentType || 'unknown'}". Supported: jpg, jpeg, png, gif, webp`,
    );
}

/**
 * Download a remote image to a per-call tmp directory. Returns the absolute
 * path on success. Caller owns the tmp dir and must clean it up. Throws
 * {@link ArgumentError} on bad input or download failure.
 *
 * @returns {Promise<{ absPath: string, cleanupDir: string }>}
 */
export async function downloadRemoteImage(imageUrl) {
    let parsed;
    try {
        parsed = new URL(imageUrl);
    } catch {
        throw new ArgumentError(`Invalid image URL: ${imageUrl}`);
    }

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Use a direct image URL (verify with `curl -sI <url>` that content-type is image/jpeg|png|gif|webp).
  2. Request a non-AVIF variant: add Accept: image/jpeg or use the original file URL many CDNs expose (e.g. strip format params).
  3. Convert the image locally first (download, `magick` convert to png/jpg) and pass a local path instead.
  4. If content-type is HTML, fix the underlying URL — it's probably a login wall or 404 page.

Example fix

// before
await postWithImageFromUrl('https://cdn.example.com/i/abc?fmt=avif'); // image/avif
// after
const res = await fetch('https://cdn.example.com/i/abc?fmt=jpg');
// confirm content-type is image/jpeg before calling
Defensive patterns

Strategy: validation

Validate before calling

const head = await fetch(imageUrl, { method: 'HEAD' });
const type = head.headers.get('content-type') || '';
if (!/^image\/(jpeg|png|gif|webp)$/.test(type)) {
  throw new Error(`URL serves "${type}", need jpeg/png/gif/webp: ${imageUrl}`);
}

Try / catch

try {
  await postWithImageFromUrl(imageUrl);
} catch (err) {
  if (err instanceof ArgumentError && /Unsupported remote image format/.test(err.message)) {
    const local = await downloadAndConvertToPng(imageUrl);
    return postWithImage(local);
  }
  throw err;
}

Prevention

When it happens

Trigger: Downloading a remote image whose Content-Type is not a supported image type — e.g. text/html (an error/redirect page), application/octet-stream, image/avif, image/heic, or a missing content-type with a URL lacking a recognized extension.

Common situations: URL actually returns an HTML login/error page instead of an image; CDN serving AVIF via content negotiation; image hosts returning octet-stream with extensionless URLs; signed URLs whose path has no extension.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/3c729a706aee0ada. Report an issue: GitHub.