{"record":{"id":"45060aaa2d084a03","repo":"lfnovo/open-notebook","slug":"field-is-required-and-cannot-be-cleared-only-re","errorCode":null,"errorMessage":"{field} is required and cannot be cleared, only reassigned","messagePattern":"(.+?) is required and cannot be cleared, only reassigned","errorType":"exception","errorClass":"InvalidInputError","httpStatus":null,"severity":"warning","filePath":"api/routers/models.py","lineNumber":358,"sourceCode":"async def update_default_models(defaults_data: DefaultModelsResponse):\n    \"\"\"Update default model assignments.\n\n    Partial-update semantics keyed on field PRESENCE, not value: a field\n    absent from the payload is left untouched, while an explicit null clears\n    the default (except required ones). `is not None` checks would silently\n    ignore a null sent to clear a default — the old value survived while the\n    client saw success (same anti-pattern fixed for credentials in #1046).\n    \"\"\"\n    try:\n        defaults = await DefaultModels.get_instance()\n\n        sent = defaults_data.model_fields_set\n        for field in DefaultModelsResponse.model_fields:\n            if field not in sent:\n                continue\n            value = getattr(defaults_data, field)\n            if value is None and field in REQUIRED_DEFAULTS:\n                raise InvalidInputError(\n                    f\"{field} is required and cannot be cleared, only reassigned\"\n                )\n            setattr(defaults, field, value)\n\n        await defaults.update()\n\n        # No cache refresh needed - next access will fetch fresh data from DB\n\n        return DefaultModelsResponse(\n            default_chat_model=defaults.default_chat_model,  # type: ignore[attr-defined]\n            default_transformation_model=defaults.default_transformation_model,  # type: ignore[attr-defined]\n            large_context_model=defaults.large_context_model,  # type: ignore[attr-defined]\n            default_text_to_speech_model=defaults.default_text_to_speech_model,  # type: ignore[attr-defined]\n            default_speech_to_text_model=defaults.default_speech_to_text_model,  # type: ignore[attr-defined]\n            default_embedding_model=defaults.default_embedding_model,  # type: ignore[attr-defined]\n            default_tools_model=defaults.default_tools_model,  # type: ignore[attr-defined]\n        )\n    except HTTPException:","sourceCodeStart":340,"sourceCodeEnd":376,"githubUrl":"https://github.com/lfnovo/open-notebook/blob/a7de90d38aaf18ee85fd661854d35c11e44613e2/api/routers/models.py#L340-L376","documentation":"InvalidInputError (mapped to 400) from PATCH/PUT /api/v1/models/defaults when a request explicitly sets one of the REQUIRED_DEFAULTS fields to null. Required defaults (e.g. chat_model) may be reassigned but never cleared, since the app cannot function without them.","triggerScenarios":"PATCH /models/defaults with {\"chat_model\": null} or a partial body that includes a required field as null.","commonSituations":"Frontend sending the whole form with empty selections; generic 'clear settings' UI flow; JSON serializers that emit null for empty inputs.","solutions":["Send a different valid model id instead of null for required fields","Omit required fields from the patch body when not changing them","In the client, disable 'clear' for required defaults and only allow reassignment"],"exampleFix":"// before\n{\"chat_model\": null, \"embedding_model\": \"text-embedding-3-small\"}\n// after\n{\"chat_model\": \"gpt-4o-mini\", \"embedding_model\": \"text-embedding-3-small\"}","handlingStrategy":"validation","validationCode":"const REQUIRED = ['chat_model']; // subset of REQUIRED_DEFAULTS\nfor (const k of REQUIRED) if (patch[k] === null) delete patch[k]; // or reject\nawait api.updateDefaults(patch);","typeGuard":"const hasNullRequired = (p: Record<string,string|null>, req: string[]) => req.some(k => p[k] === null);","tryCatchPattern":"try { await api.updateDefaults(patch); } catch (e) { if (e.status === 400 && /cannot be cleared/.test(e.detail)) promptReassign(); }","preventionTips":["Never send null for required defaults","Omit unchanged fields from patches","Disable 'clear' buttons on required defaults in UI"],"tags":["models","defaults","validation","http-400"],"backgroundTag":"required-field-null-rejection","analyzedSha":"a7de90d38aaf18ee85fd661854d35c11e44613e2","analyzedAt":"2026-08-27T02:39:58.166Z","schemaVersion":2},"datasetVersion":"2026-08-27T03:17:27.898Z"}