{"record":{"id":"6c93cb833b9af2fd","repo":"lfnovo/open-notebook","slug":"failed-to-update-credential","errorCode":null,"errorMessage":"Failed to update credential","messagePattern":"Failed to update credential","errorType":"http","errorClass":"HTTPException","httpStatus":500,"severity":"error","filePath":"api/routers/credentials.py","lineNumber":302,"sourceCode":"\n        try:\n            ensure_provider_required_fields(cred)\n        except ValueError as e:\n            raise _handle_value_error(e)\n\n        await cred.save()\n        models = await cred.get_linked_models()\n        return credential_to_response(cred, len(models))\n\n    except HTTPException:\n        raise\n    except NotFoundError:\n        raise HTTPException(status_code=404, detail=\"Credential not found\")\n    except OpenNotebookError:\n        raise\n    except Exception as e:\n        logger.error(f\"Error updating credential {credential_id}: {e}\")\n        raise HTTPException(status_code=500, detail=\"Failed to update credential\")\n\n\n@router.delete(\"/{credential_id}\", response_model=CredentialDeleteResponse)\nasync def delete_credential(\n    credential_id: str,\n    migrate_to: Optional[str] = Query(\n        None, description=\"Migrate linked models to this credential ID\"\n    ),\n):\n    \"\"\"\n    Delete a credential.\n\n    If the credential has linked models:\n    - Pass migrate_to=<credential_id> to reassign them to another credential\n    - Otherwise, linked models are cascade-deleted automatically\n    \"\"\"\n    try:\n        try:","sourceCodeStart":284,"sourceCodeEnd":320,"githubUrl":"https://github.com/lfnovo/open-notebook/blob/a7de90d38aaf18ee85fd661854d35c11e44613e2/api/routers/credentials.py#L284-L320","documentation":"Catch-all 500 from PUT /api/credentials/{credential_id}. Raised when the update fails with an unexpected exception beyond the explicit NotFoundError and OpenNotebookError handlers — typically a DB write error or failure while re-fetching/updating linked models.","triggerScenarios":"Updating a credential (e.g. changing its api_key or clearing base_url) while the DB write fails, or when get_linked_models() errors after a successful update, producing a 500 even though the test test_update_rejects_clearing_compatible_base_url exercises this path.","commonSituations":"DB connection dropped mid-request, schema mismatch on updated fields, or encryption errors when re-encrypting a rotated API key.","solutions":["Check logs for 'Error updating credential <id>: ...' — the update may have partially applied; re-fetch to verify state","Confirm DB health and retry the PUT idempotently","If re-encrypting a key, verify OPEN_NOTEBOOK_ENCRYPTION_KEY is stable across requests","If linked-model fetch failed after update, reload the credential to see the persisted state before retrying"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await api.updateCredential(id, patch);\n} catch (e) {\n  if (e.status === 500) {\n    const fresh = await api.getCredential(id).catch(() => null); // did it partially apply?\n    if (fresh && matches(fresh, patch)) return fresh; // idempotent recovery\n  }\n  throw e;\n}","preventionTips":["Design update payloads to be idempotent so retries are safe","Verify DB health before large credential edits"],"tags":["credentials","update","http-500","database"],"backgroundTag":"http-500-fallback","analyzedSha":"a7de90d38aaf18ee85fd661854d35c11e44613e2","analyzedAt":"2026-08-27T02:39:58.166Z","schemaVersion":2},"datasetVersion":"2026-08-27T03:17:27.898Z"}