{"record":{"id":"3eba6870213c20ed","repo":"unslothai/unsloth","slug":"cannot-apply-partial-settings-patch-to-corrupt-key","errorCode":null,"errorMessage":"Cannot apply partial settings patch to corrupt key(s): {keys}","messagePattern":"Cannot apply partial settings patch to corrupt key\\(s\\): (.+?)","errorType":"exception","errorClass":"CorruptSettingsError","httpStatus":409,"severity":"error","filePath":"studio/backend/storage/studio_db.py","lineNumber":3692,"sourceCode":"    \"\"\"Atomic read-merge-write under BEGIN IMMEDIATE so concurrent writers\n    cannot drop each other's updates.\"\"\"\n    if not updates:\n        return list_chat_settings()\n    conn = get_connection()\n    try:\n        conn.execute(\"BEGIN IMMEDIATE\")\n        current, corrupt = _load_chat_settings_for_merge(conn)\n        # An atomic key carries its whole value, so it repairs a quarantined row\n        # rather than patching a base that is no longer there.\n        unsafe_partial_keys = [\n            key\n            for key, value in updates.items()\n            if key in corrupt and isinstance(value, dict) and key not in _ATOMIC_SETTING_KEYS\n        ]\n        if unsafe_partial_keys:\n            conn.commit()\n            keys = \", \".join(sorted(unsafe_partial_keys))\n            raise CorruptSettingsError(\n                f\"Cannot apply partial settings patch to corrupt key(s): {keys}\"\n            )\n        merged = _deep_merge_settings(current, updates)\n        now = datetime.now(timezone.utc).isoformat()\n        conn.executemany(\n            \"\"\"\n            INSERT INTO chat_settings (key, value_json, updated_at)\n            VALUES (?, ?, ?)\n            ON CONFLICT(key) DO UPDATE SET\n                value_json = excluded.value_json,\n                updated_at = excluded.updated_at\n            \"\"\",\n            [(key, json.dumps(value), now) for key, value in merged.items()],\n        )\n        conn.commit()\n        return merged\n    except CorruptSettingsError:\n        raise","sourceCodeStart":3674,"sourceCodeEnd":3710,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/storage/studio_db.py#L3674-L3710","documentation":"CorruptSettingsError raised by the chat settings merge path when a partial (dict-valued) update targets a key whose stored row has been quarantined as corrupt. A corrupt row has no trustworthy base to deep-merge into, so a partial patch is refused; only atomic keys (members of _ATOMIC_SETTING_KEYS) or full-value replacements are allowed because they overwrite the whole row and effectively repair it. The transaction is committed (releasing the IMMEDIATE lock) before raising.","triggerScenarios":"Calling update_chat_settings(updates={'someNestedSetting': {'a': 1}}) where 'someNestedSetting' is currently flagged corrupt by _load_chat_settings_for_merge() and is not in _ATOMIC_SETTING_KEYS.","commonSituations":"A settings row was corrupted by a crash mid-write or hand-edited JSON; afterwards every partial settings save from the UI fails until the key is reset or sent whole. Common after DB migration bugs or disk-full events during a previous write.","solutions":["Send the FULL value for the corrupt key(s) instead of a partial dict, which replaces and repairs the quarantined row","Delete/reset the corrupt setting row so the next merge starts from a clean base","Inspect the quarantined keys (the error message lists them) and decide per key whether to restore defaults or write a complete replacement"],"exampleFix":"// before (partial patch on a corrupt key -> raises)\nupdate_chat_settings({'training_defaults': {'lr': 1e-4}})\n\n// after (full replacement repairs the row)\nupdate_chat_settings({'training_defaults': {'lr': 1e-4, 'epochs': 3, 'batch': 8}})","handlingStrategy":"try-catch","validationCode":"def is_safe_patch(updates: dict, atomic_keys: set[str]) -> bool:\n    \"\"\"A patch is safe if no dict-valued update targets a non-atomic key.\n    (Corrupt-key awareness lives server-side; this is the structural guard.)\"\"\"\n    return all(\n        not isinstance(v, dict) or k in atomic_keys\n        for k, v in updates.items()\n    )","typeGuard":null,"tryCatchPattern":"from studio.backend.storage.studio_db import CorruptSettingsError\n\ntry:\n    update_chat_settings(updates)\nexcept CorruptSettingsError as e:\n    # e names the corrupt keys; re-send FULL values for those keys to repair\n    corrupt = parse_keys_from_message(str(e))\n    update_chat_settings({k: full_defaults[k] for k in corrupt})\n    update_chat_settings(updates)","preventionTips":["Send whole values (not partial dicts) for settings that may have been quarantined","After any crash during a settings write, check for quarantined keys before the next save","Log the key list from the error message — it tells you exactly which rows to repair or reset"],"tags":["settings","corruption","transaction","studio-backend"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}