Skip to content

Commit 940f632

Browse files
2sumtechclaude
andcommitted
fix(image): fail when every generated image is rejected
The image generation API answers HTTP 200 with an empty `image_urls` list and `success_count: 0` when all requested images are rejected by the content policy or fail in the model. `mmx image generate` ignored `success_count`, so it pushed the `--out` path onto `saved`, printed it as a successful result and exited 0 without writing any file. ERRORS.md already documents the intended behaviour for this scenario: | `success_count === 0` (all rejected) | `Image generation failed: all images were rejected (content policy or model error).` | Reject the response before the download/write step so the documented error and a non-zero exit code are produced instead of a phantom path. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
1 parent 33453cf commit 940f632

2 files changed

Lines changed: 40 additions & 0 deletions

File tree

‎src/commands/image/generate.ts‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,16 @@ export default defineCommand({
142142
process.stderr.write('[Model: image-01]\n');
143143
}
144144

145+
// The API answers 200 with an empty image list when every image is rejected.
146+
// Without this check the command reports the --out path as saved and exits 0
147+
// even though nothing was written (see ERRORS.md, `mmx image generate`).
148+
if (response.data.success_count === 0) {
149+
throw new CLIError(
150+
'Image generation failed: all images were rejected (content policy or model error).',
151+
ExitCode.GENERAL,
152+
);
153+
}
154+
145155
const saved: string[] = [];
146156

147157
if (outPath) {

‎test/commands/image/generate.test.ts‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import { describe, it, expect } from 'bun:test';
2+
import { existsSync, rmSync } from 'fs';
3+
import { tmpdir } from 'os';
4+
import { join } from 'path';
25
import { default as generateCommand } from '../../../src/commands/image/generate';
6+
import { createMockServer, jsonResponse } from '../../helpers/mock-server';
37

48
const baseConfig = {
59
apiKey: 'test-key',
@@ -91,4 +95,30 @@ describe('image generate command', () => {
9195
expect(parsed.request.seed).toBe(42);
9296
expect(parsed.request.model).toBe('image-01');
9397
});
98+
99+
it('fails when the API rejects every image instead of reporting a saved file', async () => {
100+
const server = createMockServer({
101+
routes: {
102+
'/v1/image_generation': () => jsonResponse({
103+
id: 'req-1',
104+
data: { image_urls: [], task_id: 'task-1', success_count: 0, failed_count: 1 },
105+
base_resp: { status_code: 0, status_msg: 'success' },
106+
}),
107+
},
108+
});
109+
const outPath = join(tmpdir(), `mmx-image-rejected-${process.pid}-${Date.now()}.jpg`);
110+
111+
try {
112+
await expect(
113+
generateCommand.execute(
114+
{ ...baseConfig, baseUrl: server.url, quiet: true },
115+
{ ...baseFlags, quiet: true, prompt: 'A cat', out: outPath },
116+
),
117+
).rejects.toThrow('Image generation failed: all images were rejected');
118+
expect(existsSync(outPath)).toBe(false);
119+
} finally {
120+
rmSync(outPath, { force: true });
121+
server.close();
122+
}
123+
});
94124
});

0 commit comments

Comments
 (0)