[PR #21398] [CLOSED] fix: JSONField double deserialization on PostgreSQL #64922

Closed
opened 2026-05-06 10:39:42 -05:00 by GiteaMirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/open-webui/open-webui/pull/21398
Author: @lewisco
Created: 2/13/2026
Status: Closed

Base: devHead: fix/jsonfield-double-deserialization


📝 Commits (1)

  • 9a55aa6 fix: handle already-deserialized JSON values from PostgreSQL

📊 Changes

1 file changed (+5 additions, -2 deletions)

View changed files

📝 backend/open_webui/internal/db.py (+5 -2)

📄 Description

Description

  • Fix JSONField.process_result_value crashing with TypeError on PostgreSQL when psycopg2 returns already-deserialized Python dicts/lists from JSONB columns. The bug surfaces most visibly when creating a new skill, as db.refresh() triggers deserialization of the meta column.

Discussion: https://github.com/open-webui/open-webui/discussions/21397

Added

  • N/A

Changed

  • N/A

Deprecated

  • N/A

Removed

  • N/A

Fixed

  • JSONField.process_result_value now checks whether the value is already a dict or list before calling json.loads(). On PostgreSQL with psycopg2's native JSON type adaptation, JSONB columns return Python objects directly. The previous unconditional json.loads(value) raised TypeError: the JSON object must be str, bytes or bytearray, not dict.

Security

  • N/A

Breaking Changes

  • N/A

Additional Information

  • Single file change: backend/open_webui/internal/db.py, 3 lines added, 2 removed
  • SQLite is unaffected (always returns JSON as text strings), but the fix is safe for both backends
  • The Peewee equivalent python_value (same file, lines 51-53) has the same unconditional json.loads but is left out of scope to keep this minimal
  • Co-authored with AI, manually reviewed and tested on both PostgreSQL and SQLite

Testing

PostgreSQL (live deployment, psycopg2 driver):

  • Skill creation works (previously crashed on db.refresh())
  • All JSONB column reads return correctly

SQLite (local Docker, ghcr.io/open-webui/open-webui:main with patched file bind-mounted):

Test JSONField columns hit Result
Create skill (with tags in meta) skill.meta PASS
Read skill back skill.meta PASS
List skills (multiple reads) skill.meta PASS
Create chat chat.chat PASS
Read chat back chat.chat PASS
Update chat (nested dicts, lists, child objects) chat.chat PASS
Update user settings user.settings PASS
Read user settings back user.settings PASS
Empty lists, null values, empty dicts chat.chat PASS

On SQLite the isinstance guard never triggers because values always arrive as strings, so json.loads() runs exactly as before. Zero errors in container logs.

Screenshots or Videos

N/A (backend-only change, no UI impact)

Contributor License Agreement

By submitting this pull request, I confirm that I have read and fully agree to the Contributor License Agreement (CLA), and I am providing my contributions under its terms.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/open-webui/open-webui/pull/21398 **Author:** [@lewisco](https://github.com/lewisco) **Created:** 2/13/2026 **Status:** ❌ Closed **Base:** `dev` ← **Head:** `fix/jsonfield-double-deserialization` --- ### 📝 Commits (1) - [`9a55aa6`](https://github.com/open-webui/open-webui/commit/9a55aa63e8c040c96f93a347baace8ea877d12f4) fix: handle already-deserialized JSON values from PostgreSQL ### 📊 Changes **1 file changed** (+5 additions, -2 deletions) <details> <summary>View changed files</summary> 📝 `backend/open_webui/internal/db.py` (+5 -2) </details> ### 📄 Description ### Description - Fix `JSONField.process_result_value` crashing with `TypeError` on PostgreSQL when psycopg2 returns already-deserialized Python dicts/lists from JSONB columns. The bug surfaces most visibly when creating a new skill, as `db.refresh()` triggers deserialization of the `meta` column. Discussion: https://github.com/open-webui/open-webui/discussions/21397 ### Added - N/A ### Changed - N/A ### Deprecated - N/A ### Removed - N/A ### Fixed - `JSONField.process_result_value` now checks whether the value is already a `dict` or `list` before calling `json.loads()`. On PostgreSQL with psycopg2's native JSON type adaptation, JSONB columns return Python objects directly. The previous unconditional `json.loads(value)` raised `TypeError: the JSON object must be str, bytes or bytearray, not dict`. ### Security - N/A ### Breaking Changes - N/A --- ### Additional Information - Single file change: `backend/open_webui/internal/db.py`, 3 lines added, 2 removed - SQLite is unaffected (always returns JSON as text strings), but the fix is safe for both backends - The Peewee equivalent `python_value` (same file, lines 51-53) has the same unconditional `json.loads` but is left out of scope to keep this minimal - Co-authored with AI, manually reviewed and tested on both PostgreSQL and SQLite ### Testing **PostgreSQL (live deployment, psycopg2 driver):** - Skill creation works (previously crashed on `db.refresh()`) - All JSONB column reads return correctly **SQLite (local Docker, `ghcr.io/open-webui/open-webui:main` with patched file bind-mounted):** | Test | JSONField columns hit | Result | |------|----------------------|--------| | Create skill (with tags in meta) | `skill.meta` | PASS | | Read skill back | `skill.meta` | PASS | | List skills (multiple reads) | `skill.meta` | PASS | | Create chat | `chat.chat` | PASS | | Read chat back | `chat.chat` | PASS | | Update chat (nested dicts, lists, child objects) | `chat.chat` | PASS | | Update user settings | `user.settings` | PASS | | Read user settings back | `user.settings` | PASS | | Empty lists, null values, empty dicts | `chat.chat` | PASS | On SQLite the `isinstance` guard never triggers because values always arrive as strings, so `json.loads()` runs exactly as before. Zero errors in container logs. ### Screenshots or Videos N/A (backend-only change, no UI impact) ### Contributor License Agreement By submitting this pull request, I confirm that I have read and fully agree to the [Contributor License Agreement (CLA)](https://github.com/open-webui/open-webui/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT), and I am providing my contributions under its terms. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
GiteaMirror added the pull-request label 2026-05-06 10:39:42 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/open-webui#64922