{"record":{"id":"9a31315d56b570da","repo":"invoke-ai/InvokeAI","slug":"invalid-preset-data","errorCode":null,"errorMessage":"Invalid preset data","messagePattern":"Invalid preset data","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"invokeai/app/api/routers/style_presets.py","lineNumber":120,"sourceCode":"    image: Optional[UploadFile] = File(description=\"The image file to upload\", default=None),\n    style_preset_id: str = Path(description=\"The id of the style preset to update\"),\n    data: str = Form(description=\"The data of the style preset to update\"),\n) -> StylePresetRecordWithImage:\n    \"\"\"Updates a style preset\"\"\"\n    # Validate the data payload BEFORE any image-state mutation so a malformed\n    # request can't leave the preset image partially updated.\n    try:\n        parsed_data = json.loads(data)\n        validated_data = StylePresetFormData(**parsed_data)\n\n        name = validated_data.name\n        type = validated_data.type\n        positive_prompt = validated_data.positive_prompt\n        negative_prompt = validated_data.negative_prompt\n        is_public = validated_data.is_public\n\n    except (json.JSONDecodeError, pydantic.ValidationError):\n        raise HTTPException(status_code=400, detail=\"Invalid preset data\")\n\n    record = await asyncio.to_thread(_load_record_or_404, style_preset_id)\n    _assert_preset_write(record, current_user)\n\n    if image is not None:\n        if not image.content_type or not image.content_type.startswith(\"image\"):\n            raise HTTPException(status_code=415, detail=\"Not an image\")\n\n        contents = await image.read()\n        try:\n            pil_image = await asyncio.to_thread(Image.open, io.BytesIO(contents))\n\n        except Exception:\n            ApiDependencies.invoker.services.logger.error(traceback.format_exc())\n            raise HTTPException(status_code=415, detail=\"Failed to read image\")\n\n        try:\n            await asyncio.to_thread(","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/app/api/routers/style_presets.py#L102-L138","documentation":"update_style_preset (PATCH style preset) parses the multipart 'data' form field as JSON and validates it against the StylePresetFormData pydantic model. If the string is not valid JSON or the parsed object fails schema validation (missing/extra/ill-typed fields), the endpoint returns HTTP 400 with detail 'Invalid preset data'. It is a request-body contract error, not a server fault.","triggerScenarios":"PATCH /style_presets/i/{id} with multipart form where: data is not parseable JSON (truncated, single quotes, HTML error page), data omits a required StylePresetFormData field (name, type, positive_prompt, negative_prompt, is_public), or a field has the wrong type (e.g. is_public as string 'true' instead of boolean, unknown preset type enum value).","commonSituations":"Client builds the data field with JSON.stringify omitted (sends '[object Object]' or a Python dict repr); content-type confusion causing double-encoded JSON strings; SDK version drift where StylePresetFormData gained or renamed fields; hand-written curl tests sending raw key=value instead of JSON.","solutions":["Print/log the exact 'data' string sent and run json.loads on it client-side to confirm it is valid JSON.","Validate the payload against StylePresetFormData (name, type, positive_prompt, negative_prompt, is_public) with the same pydantic model or the generated OpenAPI client type before sending.","Ensure the field is sent as a single multipart form part named 'data' containing a JSON string, not as separate form fields.","Regenerate the API client from the server's OpenAPI schema so the form schema matches the running server version."],"exampleFix":"// before\ncurl -X PATCH .../style_presets/i/abc -F name=\"My preset\" -F type=\"user\"\n\n// after\ncurl -X PATCH .../style_presets/i/abc \\\n  -F 'data={\"name\":\"My preset\",\"type\":\"user\",\"positive_prompt\":\"...\",\"negative_prompt\":\"...\",\"is_public\":false}'","handlingStrategy":"validation","validationCode":"import json\n\ndef validate_preset_payload(data_str: str) -> dict:\n    payload = json.loads(data_str)  # raises JSONDecodeError -> would be 400\n    required = {\"name\": str, \"type\": str, \"positive_prompt\": str, \"negative_prompt\": str, \"is_public\": bool}\n    for key, typ in required.items():\n        assert key in payload and isinstance(payload[key], typ), f\"bad field: {key}\"\n    return payload\n\nvalidate_preset_payload(data_field)  # run before PATCH/POST","typeGuard":"def is_valid_preset_data(v: object) -> TypeGuard[dict]:\n    return (\n        isinstance(v, dict)\n        and isinstance(v.get(\"name\"), str)\n        and isinstance(v.get(\"type\"), str)\n        and isinstance(v.get(\"positive_prompt\"), str)\n        and isinstance(v.get(\"negative_prompt\"), str)\n        and isinstance(v.get(\"is_public\"), bool)\n    )","tryCatchPattern":"try {\n  await api.updateStylePreset({ stylePresetId, data: JSON.stringify(preset), image });\n} catch (e) {\n  if (e.status === 400 && e.detail === \"Invalid preset data\") {\n    console.error(\"Payload failed schema:\", JSON.stringify(preset));\n  } else throw e;\n}","preventionTips":["Always JSON.stringify the preset object into the single 'data' form field.","Use the generated OpenAPI client instead of hand-built form bodies.","Validate against StylePresetFormData fields locally before every send.","Round-trip test: json.loads(JSON.stringify(preset)) must succeed before upload."],"tags":["fastapi","pydantic","http-400","request-validation"],"backgroundTag":"schema-validation-failed","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}