Skip to content

Commit b6e6a95

Browse files
authored
Fix FAL img2img parameter mappings for image_urls array endpoints (#183)
# Fix FAL img2img parameter mappings for image_urls array endpoints ## Summary Fixed 422 (Unprocessable Content) errors for 4 FAL img2img endpoints that were failing during polling. The root cause was parameter schema mismatches - these endpoints expect `image_urls` (array) but we were sending `image_url` (string). **Changes:** - **Fixed 4 endpoints**: gemini-25-flash-image/edit, bytedance/seedream/v4/edit, wan-25-preview/image-to-image, gpt-image-1-mini/edit - Updated initialInput to use `image_urls: []` instead of `image_url: ""` - Added `inputMap: { image_url: "image_urls" }` to map our canonical field names to endpoint-specific names - **Updated prepareFalInput** in mutations.ts: - Now applies endpoint's `inputMap` before processing (maps `image_url` → `image_urls`) - Converts string values to arrays when the target key is `image_urls` - **Fixed ideogram/v3/edit**: Removed `inputAsset` since this endpoint only supports inpainting (requires `mask_url` parameter, not suitable for basic img2img) The issue was that POST requests were being accepted (IN_QUEUE status), but jobs failed during worker processing when validation discovered invalid parameter types. The 422 errors during GET polling were symptoms of job failure, not polling issues. ## Review & Testing Checklist for Human **Priority**: 🟡 Yellow (Medium Risk - untested API integration changes) - [ ] **Test the 4 fixed endpoints** with img2img operations (upload image, add prompt, generate): - fal-ai/gemini-25-flash-image/edit - fal-ai/bytedance/seedream/v4/edit - fal-ai/wan-25-preview/image-to-image - fal-ai/gpt-image-1-mini/edit - Verify jobs complete successfully without 422 errors - [ ] **Test flux-kontext-lora** (was working before) - ensure inputMap logic doesn't break working endpoints - [ ] **Verify ideogram/v3/edit** no longer appears in img2img UI (correctly hidden since it requires mask for inpainting) - [ ] Check preview deployment and test with real FAL API key ### Test Plan 1. Open the app in preview deployment 2. For each of the 4 fixed endpoints: Upload a test image, enter a prompt like "make it ice", click generate 3. Watch the console - should see jobs complete without 422 errors 4. Verify the working endpoint (flux-kontext-lora) still works correctly ### Notes - Changes based on schema analysis from `data/fal_models_schemas.json` (extracted exact parameter requirements for each endpoint) - All lint checks passed - **Risk**: Changes not tested end-to-end with real API calls - verification needed in preview deployment **Link to Devin run**: https://app.devin.ai/sessions/22310748f3b04363b4e1e0d6a2da0e08 **Requested by**: Ilya Timonin (timoncool@gmail.com) / @timoncool
2 parents 27f248d + 9c9f2c9 commit b6e6a95

2 files changed

Lines changed: 31 additions & 8 deletions

File tree

‎src/data/mutations.ts‎

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,9 +162,20 @@ export const useJobCreator = ({
162162
const prepareFalInput = async (
163163
rawInput: Record<string, unknown>,
164164
): Promise<Record<string, unknown>> => {
165+
let mappedInput = rawInput;
166+
if (endpoint?.inputMap) {
167+
const { mapInputKey } = await import("@/lib/utils");
168+
mappedInput = mapInputKey(rawInput, endpoint.inputMap);
169+
}
170+
165171
const entries = await Promise.all(
166-
Object.entries(rawInput).map(async ([key, value]) => {
172+
Object.entries(mappedInput).map(async ([key, value]) => {
167173
const resolvedValue = await uploadValueIfNeeded(key, value);
174+
175+
if (key === "image_urls" && typeof resolvedValue === "string") {
176+
return [key, [resolvedValue]] as const;
177+
}
178+
168179
return [key, resolvedValue] as const;
169180
}),
170181
);

‎src/lib/fal.ts‎

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -254,10 +254,10 @@ export const AVAILABLE_ENDPOINTS: ApiInfo[] = [
254254
inputAsset: ["image"],
255255
initialInput: {
256256
prompt: "",
257-
image_url: "",
257+
image_urls: [],
258258
},
259259
inputMap: {
260-
image: "image_url",
260+
image_url: "image_urls",
261261
},
262262
},
263263
{
@@ -287,10 +287,13 @@ export const AVAILABLE_ENDPOINTS: ApiInfo[] = [
287287
inputAsset: ["image"],
288288
initialInput: {
289289
prompt: "",
290-
image_url: "",
290+
image_urls: [],
291291
defaultWidth: 2048,
292292
defaultHeight: 2048,
293293
},
294+
inputMap: {
295+
image_url: "image_urls",
296+
},
294297
hasSeed: true,
295298
hasSafetyChecker: true,
296299
},
@@ -348,7 +351,10 @@ export const AVAILABLE_ENDPOINTS: ApiInfo[] = [
348351
inputAsset: ["image"],
349352
initialInput: {
350353
prompt: "",
351-
image_url: "",
354+
image_urls: [],
355+
},
356+
inputMap: {
357+
image_url: "image_urls",
352358
},
353359
},
354360
{
@@ -406,12 +412,11 @@ export const AVAILABLE_ENDPOINTS: ApiInfo[] = [
406412
{
407413
provider: "fal",
408414
endpointId: "fal-ai/ideogram/v3/edit",
409-
label: "Ideogram V3 (Image)",
410-
description: "Image-to-image transformation with text rendering excellence",
415+
label: "Ideogram V3 (Inpainting)",
416+
description: "Inpainting with mask support - requires mask_url parameter",
411417
cost: "$0.08/image",
412418
popularity: 5,
413419
category: "image",
414-
inputAsset: ["image"],
415420
hasSeed: true,
416421
},
417422
{
@@ -689,6 +694,13 @@ export const AVAILABLE_ENDPOINTS: ApiInfo[] = [
689694
popularity: 3,
690695
category: "image",
691696
inputAsset: ["image"],
697+
initialInput: {
698+
prompt: "",
699+
image_urls: [],
700+
},
701+
inputMap: {
702+
image_url: "image_urls",
703+
},
692704
hasSeed: true,
693705
hasNegativePrompt: true,
694706
hasSafetyChecker: true,

0 commit comments

Comments
 (0)