jackwener/OpenCLI · error · CliError

EMPTY_RESPONSE

EMPTY_RESPONSE

Error message

EMPTY_RESPONSE

What it means

The yollomi face-swap command throws EMPTY_RESPONSE when /api/ai/face-swap returns no image. The code checks data.image then data.images[0]; absence of both means the swap produced no output, so it aborts rather than downloading an undefined URL.

Source

Thrown at clis/yollomi/face-swap.js:32

    description: 'Swap faces between two photos (3 credits)',
    domain: YOLLOMI_DOMAIN,
    strategy: Strategy.COOKIE,
    args: [
        { name: 'source', required: true, help: 'Source face image URL' },
        { name: 'target', required: true, help: 'Target photo URL' },
        { name: 'output', default: './yollomi-output', help: 'Output directory' },
        { name: 'no-download', type: 'boolean', default: false, help: 'Only show URL' },
    ],
    columns: ['status', 'file', 'size', 'url'],
    func: async (page, kwargs) => {
        log.status('Swapping faces...');
        const data = await yollomiPost(page, '/api/ai/face-swap', {
            swap_image: kwargs.source,
            input_image: kwargs.target,
        });
        const url = data.image || (data.images?.[0]);
        if (!url)
            throw new CliError('EMPTY_RESPONSE', 'No result', 'Make sure both images contain clear faces');
        if (kwargs['no-download'])
            return [{ status: 'swapped', file: '-', size: '-', url }];
        try {
            const filename = `yollomi_faceswap_${Date.now()}.jpg`;
            const { path: fp, size } = await downloadOutput(url, kwargs.output, filename);
            return [{ status: 'saved', file: path.relative('.', fp), size: fmtBytes(size), url }];
        }
        catch {
            return [{ status: 'download-failed', file: '-', size: '-', url }];
        }
    },
});

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Retry with clearer, front-facing photos in both images (per the hint)
  2. Verify you did not swap `--source` and `--target` arguments
  3. Ensure both image URLs/files are publicly accessible and valid formats
  4. Log the raw API response to check for ignored error fields
  5. Re-check response field names if the site API was updated

Example fix

// before
const url = data.image || (data.images?.[0]);
if (!url) throw new CliError('EMPTY_RESPONSE', 'No result', 'Make sure both images contain clear faces');
// after
if (data.error) throw new CliError('API_ERROR', data.error, 'Check face-swap API response');
const url = data.image || data.output || (data.images?.[0]);
if (!url) throw new CliError('EMPTY_RESPONSE', 'No result', 'Make sure both images contain clear faces');
Defensive patterns

Strategy: validation

Validate before calling

for (const u of [source, target]) {
  const res = await fetch(u, { method: 'HEAD' });
  if (!res.ok) throw new Error(`image not reachable: ${u}`);
}

Type guard

function hasSwappedImage(d) {
  return d != null && (typeof d.image === 'string' || (Array.isArray(d.images) && typeof d.images[0] === 'string'));
}

Try / catch

try {
  const rows = await opencli.yollomi['face-swap']({ source, target });
} catch (e) {
  if (e.code === 'EMPTY_RESPONSE') {
    console.error(`Face swap failed: ${e.hint} — check --source/--target order and photo quality`);
  } else throw e;
}

Prevention

When it happens

Trigger: POSTing { swap_image, input_image } to /api/ai/face-swap yields a 200 response without image fields — typically one of the two images has no detectable face, an image failed to fetch server-side, or the schema changed.

Common situations: Input photos where faces are too small, occluded, or non-frontal; swapped source/target arguments by mistake; images behind auth walls the server cannot fetch; site API update.

Related errors


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