Target branch: Verify that the pull request targets the dev branch.
Description: Provide a concise description of the changes made in this pull request down below.
Changelog: Ensure a changelog entry following the format of Keep a Changelog is added at the bottom of the PR description.
Testing: Performed manual tests to verify the implemented fix works as intended.
Agentic AI Code: This Pull Request has gone through additional human review AND manual testing.
Code review: Performed a self-review of the code.
Title Prefix: Prefixed with fix: to categorize this pull request.
Changelog Entry
Description
Fixes model tags validation so the /api/models endpoint and /api/v1/models/tags don't crash when tags are stored as plain strings (e.g., ["tag1", "tag2"]) instead of the expected [{"name": "tag1"}] format. Adds a Pydantic model_validator to ModelMeta that normalizes string tags to dict format on input, and adds defensive type checking in get_model_tags to handle both formats gracefully.
Supersedes #21235 (closed due to missing CLA and wrong target branch). Fixes#20819
Changed
Modified backend/open_webui/models/models.py:
Added model_validator to ModelMeta class that normalizes string tags to dict format {"name": "tag"} on input
Handles mixed/invalid tag entries by filtering and normalizing them
Added defensive type checking in get_model_tags() to handle both string and dict tag formats
Skips invalid tag entries gracefully
Fixed
Fixed AttributeError: 'str' object has no attribute 'get' crash when model tags are stored as plain strings
Fixed 500 Internal Server Error on /api/models endpoint that caused infinite loading state in UI
Fixed model tags endpoint to handle malformed data from direct API calls
Additional Information
Root Cause
Direct API calls to /api/v1/models/model/update can save tags in any format since there's no backend validation. When incorrectly formatted tags (plain strings) are stored, the get_model_tags endpoint crashes with AttributeError: 'str' object has no attribute 'get' because tag.get('name') fails on string objects. This causes a 500 error on the models list API, leaving the UI stuck in an infinite loading state.
Solution Approach
Two-layer defense:
Input normalization: Pydantic validator automatically converts string tags to dict format when data is saved
Runtime safety: Type checking in get_model_tags handles existing malformed data gracefully
Screenshots or Videos
Test Results
# Test 1: String tags get normalized to dict formatmeta=ModelMeta(tags=['tag1','tag2'])assertmeta.model_dump()['tags']==[{'name':'tag1'},{'name':'tag2'}]# PASS# Test 2: Dict tags pass through unchangedmeta=ModelMeta(tags=[{'name':'tag1'},{'name':'tag2'}])assertmeta.model_dump()['tags']==[{'name':'tag1'},{'name':'tag2'}]# PASS# Test 3: No tags - no crashmeta=ModelMeta()assert'tags'notinmeta.model_dump()# PASS# Test 4: Mixed/invalid tags - invalid entries skipped, strings normalizedmeta=ModelMeta(tags=['tag1',{'name':'tag2'},123])assertmeta.model_dump()['tags']==[{'name':'tag1'},{'name':'tag2'}]# PASS# Test 5: get_model_tags handles both formats without crashtags_set=set()fortagin[{'name':'a'},'b',42]:ifisinstance(tag,dict):tag_name=tag.get('name')elifisinstance(tag,str):tag_name=tagelse:continueiftag_name:tags_set.add(tag_name)assertsorted(tags_set)==['a','b']# PASS
Before fix: API call with string tags causes 500 Internal Server Error, UI shows infinite loading.
After fix: String tags are auto-normalized to the correct format. Existing data with string tags is handled gracefully.
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/21249
**Author:** [@veeceey](https://github.com/veeceey)
**Created:** 2/8/2026
**Status:** ❌ Closed
**Base:** `dev` ← **Head:** `fix/issue-20819-model-tags-validation-v2`
---
### 📝 Commits (2)
- [`289daf4`](https://github.com/open-webui/open-webui/commit/289daf42f00123f1aa0e1828a82dcb27c63aedf4) fix: validate model tags to prevent UI crash on malformed input
- [`32e3c7f`](https://github.com/open-webui/open-webui/commit/32e3c7f07ac7df79b09ea258b21a61380f3e3b0a) fix: remove extra blank line to pass black formatting check
### 📊 Changes
**2 files changed** (+25 additions, -6 deletions)
<details>
<summary>View changed files</summary>
📝 `backend/open_webui/models/models.py` (+15 -2)
📝 `backend/open_webui/routers/models.py` (+10 -4)
</details>
### 📄 Description
# Pull Request Checklist
- [x] **Target branch:** Verify that the pull request targets the `dev` branch.
- [x] **Description:** Provide a concise description of the changes made in this pull request down below.
- [x] **Changelog:** Ensure a changelog entry following the format of Keep a Changelog is added at the bottom of the PR description.
- [x] **Testing:** Performed manual tests to verify the implemented fix works as intended.
- [x] **Agentic AI Code:** This Pull Request has gone through additional human review AND manual testing.
- [x] **Code review:** Performed a self-review of the code.
- [x] **Title Prefix:** Prefixed with `fix:` to categorize this pull request.
# Changelog Entry
### Description
Fixes model tags validation so the `/api/models` endpoint and `/api/v1/models/tags` don't crash when tags are stored as plain strings (e.g., `["tag1", "tag2"]`) instead of the expected `[{"name": "tag1"}]` format. Adds a Pydantic `model_validator` to `ModelMeta` that normalizes string tags to dict format on input, and adds defensive type checking in `get_model_tags` to handle both formats gracefully.
Supersedes #21235 (closed due to missing CLA and wrong target branch).
Fixes #20819
### Changed
- Modified `backend/open_webui/models/models.py`:
- Added `model_validator` to `ModelMeta` class that normalizes string tags to dict format `{"name": "tag"}` on input
- Handles mixed/invalid tag entries by filtering and normalizing them
- Modified `backend/open_webui/apps/webui/routers/models.py`:
- Added defensive type checking in `get_model_tags()` to handle both string and dict tag formats
- Skips invalid tag entries gracefully
### Fixed
- Fixed `AttributeError: 'str' object has no attribute 'get'` crash when model tags are stored as plain strings
- Fixed 500 Internal Server Error on `/api/models` endpoint that caused infinite loading state in UI
- Fixed model tags endpoint to handle malformed data from direct API calls
---
### Additional Information
**Root Cause**
Direct API calls to `/api/v1/models/model/update` can save tags in any format since there's no backend validation. When incorrectly formatted tags (plain strings) are stored, the `get_model_tags` endpoint crashes with `AttributeError: 'str' object has no attribute 'get'` because `tag.get('name')` fails on string objects. This causes a 500 error on the models list API, leaving the UI stuck in an infinite loading state.
**Solution Approach**
Two-layer defense:
1. **Input normalization**: Pydantic validator automatically converts string tags to dict format when data is saved
2. **Runtime safety**: Type checking in `get_model_tags` handles existing malformed data gracefully
### Screenshots or Videos
**Test Results**
```python
# Test 1: String tags get normalized to dict format
meta = ModelMeta(tags=['tag1', 'tag2'])
assert meta.model_dump()['tags'] == [{'name': 'tag1'}, {'name': 'tag2'}] # PASS
# Test 2: Dict tags pass through unchanged
meta = ModelMeta(tags=[{'name': 'tag1'}, {'name': 'tag2'}])
assert meta.model_dump()['tags'] == [{'name': 'tag1'}, {'name': 'tag2'}] # PASS
# Test 3: No tags - no crash
meta = ModelMeta()
assert 'tags' not in meta.model_dump() # PASS
# Test 4: Mixed/invalid tags - invalid entries skipped, strings normalized
meta = ModelMeta(tags=['tag1', {'name': 'tag2'}, 123])
assert meta.model_dump()['tags'] == [{'name': 'tag1'}, {'name': 'tag2'}] # PASS
# Test 5: get_model_tags handles both formats without crash
tags_set = set()
for tag in [{'name': 'a'}, 'b', 42]:
if isinstance(tag, dict):
tag_name = tag.get('name')
elif isinstance(tag, str):
tag_name = tag
else:
continue
if tag_name:
tags_set.add(tag_name)
assert sorted(tags_set) == ['a', 'b'] # PASS
```
**Before fix:** API call with string tags causes 500 Internal Server Error, UI shows infinite loading.
**After fix:** String tags are auto-normalized to the correct format. Existing data with string tags is handled gracefully.
### 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>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
📋 Pull Request Information
Original PR: https://github.com/open-webui/open-webui/pull/21249
Author: @veeceey
Created: 2/8/2026
Status: ❌ Closed
Base:
dev← Head:fix/issue-20819-model-tags-validation-v2📝 Commits (2)
289daf4fix: validate model tags to prevent UI crash on malformed input32e3c7ffix: remove extra blank line to pass black formatting check📊 Changes
2 files changed (+25 additions, -6 deletions)
View changed files
📝
backend/open_webui/models/models.py(+15 -2)📝
backend/open_webui/routers/models.py(+10 -4)📄 Description
Pull Request Checklist
devbranch.fix:to categorize this pull request.Changelog Entry
Description
Fixes model tags validation so the
/api/modelsendpoint and/api/v1/models/tagsdon't crash when tags are stored as plain strings (e.g.,["tag1", "tag2"]) instead of the expected[{"name": "tag1"}]format. Adds a Pydanticmodel_validatortoModelMetathat normalizes string tags to dict format on input, and adds defensive type checking inget_model_tagsto handle both formats gracefully.Supersedes #21235 (closed due to missing CLA and wrong target branch).
Fixes #20819
Changed
backend/open_webui/models/models.py:model_validatortoModelMetaclass that normalizes string tags to dict format{"name": "tag"}on inputbackend/open_webui/apps/webui/routers/models.py:get_model_tags()to handle both string and dict tag formatsFixed
AttributeError: 'str' object has no attribute 'get'crash when model tags are stored as plain strings/api/modelsendpoint that caused infinite loading state in UIAdditional Information
Root Cause
Direct API calls to
/api/v1/models/model/updatecan save tags in any format since there's no backend validation. When incorrectly formatted tags (plain strings) are stored, theget_model_tagsendpoint crashes withAttributeError: 'str' object has no attribute 'get'becausetag.get('name')fails on string objects. This causes a 500 error on the models list API, leaving the UI stuck in an infinite loading state.Solution Approach
Two-layer defense:
get_model_tagshandles existing malformed data gracefullyScreenshots or Videos
Test Results
Before fix: API call with string tags causes 500 Internal Server Error, UI shows infinite loading.
After fix: String tags are auto-normalized to the correct format. Existing data with string tags is handled gracefully.
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.