⚠️ This issue respects the following points: ⚠️
Bug description
OC\SystemTag\SystemTagManager truncates tag names to 64 bytes using the
byte-based substr($tagName, 0, 64). When the tag name contains multibyte
UTF-8 characters (e.g. Cyrillic, 2 bytes per character) and the 64th byte
falls inside a character, the truncated string ends with a dangling lead
byte (e.g. \xD0) — invalid UTF-8. MariaDB/MySQL in strict mode then rejects
the INSERT with error 1366, which surfaces to the user as an HTTP 500 on
POST /remote.php/dav/systemtags from the web UI.
There are three affected call sites (line numbers from v34.0.3):
| Line |
Method |
Code |
| 139 |
getTag() |
$truncatedTagName = substr($tagName, 0, 64); |
| 184 |
createTag() |
$truncatedTagName = substr($tagName, 0, 64); |
| 249 |
updateTag() |
$truncatedNewName = substr($newName, 0, 64); |
Two distinct user-visible consequences:
- Hard failure (500): when the byte-64 cut splits a multibyte character
→ SQLSTATE 22007 / MySQL error 1366, unhandled, returned as 500. Any
Cyrillic tag name of 33+ characters can trigger this. Users see a generic
error and typically retry several times (visible in our access logs).
- Silent corruption: when the cut happens to land on a character
boundary, a mangled, mid-word-truncated tag is created without any warning
(real examples from our instance: 8 ерий alongside the intended
8 серий; Для платформ, триллер, черная комед). This also creates
near-duplicate tags that fragment file/tag associations.
Root cause:
substr() counts bytes and cannot know about UTF-8 character boundaries.
Since the limit here is a byte budget (the column is varchar(64)),
the correct function is mb_strcut(), which enforces the same byte limit
but backs off to the nearest character boundary and never emits an invalid
sequence.
Note: mb_substr() is not a correct replacement — it counts characters,
so 64 Cyrillic characters would be 128 bytes and overflow the column.
Proposed fix:
Replace all three occurrences:
// getTag(), createTag():
$truncatedTagName = mb_strcut($tagName, 0, 64, 'UTF-8');
// updateTag():
$truncatedNewName = mb_strcut($newName, 0, 64, 'UTF-8');
We have been running this patch in production since 2026-08-27: the 500 is
gone, over-long names are truncated at a clean character boundary, and
ASCII-only behavior is unchanged (mb_strcut is byte-identical to substr
for single-byte input). Happy to submit this as a PR if the approach is
acceptable.
A possible follow-up (out of scope for this fix): silently truncating user
input at all is questionable — returning a proper 400 with a meaningful
message, or letting the WebDAV endpoint reject over-long names, might be
preferable UX. The mb_strcut change is the minimal correctness fix either
way.
Steps to reproduce
- Use MySQL/MariaDB with strict mode (default) and utf8mb4 (default;
oc_systemtag is utf8mb4_bin — charset configuration is not the
issue, the payload is genuinely invalid UTF-8 after truncation).
- Run:
# 63 ASCII bytes + one 2-byte Cyrillic letter => byte 64 splits "б"
sudo -u www-data php occ tag:add "$(python3 -c 'print("a"*63 + "б")')" public
Equivalently via the web UI: create a tag whose name is 63 a characters
followed by б (or any Cyrillic name where character 33 starts at
byte 64, e.g. many real-world descriptive Cyrillic tag names).
Expected behavior
Tag is created with the name truncated at a character boundary (≤64 bytes,
no split character) — or, arguably better, the request is rejected with a
clear validation error. In no case a 500.
Actual behavior:
An exception occurred while executing a query:
SQLSTATE[22007]: Invalid datetime format: 1366
Incorrect string value: '\xD0' for column <db>.oc_systemtag.name at row 1
`occ tag:add` throws the exception; the web UI request
`POST /remote.php/dav/systemtags` returns HTTP 500.
### Nextcloud Server version
34
### Operating system
Debian/Ubuntu
### PHP engine version
PHP 8.3
### Web server
Apache (supported)
### Database engine version
MariaDB
### Is this bug present after an update or on a fresh install?
Updated from a MINOR version (ex. 32.0.1 to 32.0.2)
### Are you using the Nextcloud Server Encryption module?
None
### What user-backends are you using?
- [x] Default user-backend _(database)_
- [ ] LDAP/ Active Directory
- [ ] SSO - SAML
- [ ] Other
### Configuration report
```json
{
"system": {
"serverid": "1",
"instanceid": "***REMOVED SENSITIVE VALUE***",
"passwordsalt": "***REMOVED SENSITIVE VALUE***",
"secret": "***REMOVED SENSITIVE VALUE***",
"allow_local_remote_servers": true,
"trusted_domains": [
"10.0.5.79",
"cd.m-pr.tv",
"localhost",
"10.0.4.72",
"localhost",
"talk.m-pr.tv"
],
"push_server_url": "https:\/\/cd.m-pr.tv\/push",
"datadirectory": "***REMOVED SENSITIVE VALUE***",
"dbtype": "mysql",
"version": "34.0.3.2",
"overwrite.cli.url": "https:\/\/cd.m-pr.tv",
"dbname": "***REMOVED SENSITIVE VALUE***",
"dbhost": "***REMOVED SENSITIVE VALUE***",
"dbport": "",
"dbtableprefix": "oc_",
"mysql.utf8mb4": true,
"dbuser": "***REMOVED SENSITIVE VALUE***",
"dbpassword": "***REMOVED SENSITIVE VALUE***",
"installed": true,
"maintenance": false,
"memcache.local": "\\OC\\Memcache\\APCu",
"memcache.distributed": "\\OC\\Memcache\\Redis",
"redis": {
"host": "***REMOVED SENSITIVE VALUE***",
"port": 6379
},
"memcache.locking": "\\OC\\Memcache\\Redis",
"theme": "",
"loglevel": 2,
"overwriteprotocol": "https",
"mail_smtpmode": "smtp",
"mail_sendmailmode": "smtp",
"mail_from_address": "***REMOVED SENSITIVE VALUE***",
"mail_domain": "***REMOVED SENSITIVE VALUE***",
"mail_smtphost": "***REMOVED SENSITIVE VALUE***",
"mail_smtpport": "587",
"default_locale": "ru_RU",
"default_phone_region": "RU",
"app_install_overwrite": {
"0": "documentserver_community",
"2": "files_rightclick",
"3": "metadata",
"4": "files_archive"
},
"enable_previews": true,
"preview_libreoffice_path": "\/usr\/bin\/libreoffice",
"apps_paths": [
{
"path": "\/var\/www\/html\/nextcloud\/apps",
"url": "\/apps",
"writable": false
},
{
"path": "\/var\/www\/html\/nextcloud\/custom_apps",
"url": "\/custom_apps",
"writable": true
}
],
"mail_smtpauth": true,
"mail_smtpname": "***REMOVED SENSITIVE VALUE***",
"mail_smtppassword": "***REMOVED SENSITIVE VALUE***",
"maintenance_window_start": 22,
"collabora\/code": "https:\/\/cd.m-pr.tv",
"fileuploadnew.chunk_size": "1G",
"fileuploadnew.max_filesize": "25G",
"memory_limit": "26G",
"timeout": 36000,
"enable_push": true,
"has_internet_connection": true,
"notify_push_base_endpoint": "https:\/\/cd.m-pr.tv\/push",
"overwritewebroot": "",
"mail_smtpauthtype": "LOGIN",
"mail_smtpstreamoptions": {
"ssl": {
"allow_self_signed": false,
"verify_peer": true,
"verify_peer_name": true
}
},
"logtimezone": "Europe\/Moscow",
"mail_smtptimeout": 60,
"trusted_proxies": "***REMOVED SENSITIVE VALUE***",
"overwritehost": "cd.m-pr.tv",
"preview_imaginary_url": "***REMOVED SENSITIVE VALUE***",
"enabledPreviewProviders": [
"OC\\Preview\\Imaginary",
"OC\\Preview\\ImaginaryPDF",
"OC\\Preview\\TXT",
"OC\\Preview\\MarkDown",
"OC\\Preview\\OpenDocument",
"OC\\Preview\\MSOffice2003",
"OC\\Preview\\MSOfficeDoc",
"OC\\Preview\\MSOffice2007",
"OC\\Preview\\Photoshop",
"OC\\Preview\\Font",
"OC\\Preview\\MP3",
"OC\\Preview\\Movie"
],
"preview_max_x": 2048,
"preview_max_y": 2048,
"preview_concurrency_all": 8,
"preview_concurrency_new": 4
}
}
List of activated Apps
Enabled:
- activity: 7.0.0
- admin_audit: 1.24.0
- admincockpit: 1.3.9
- analytics: 6.8.0
- announcementbanner: 2.7.0
- appstore: 1.0.0
- bruteforcesettings: 7.0.0
- calendar: 6.5.4
- cloud_federation_api: 1.18.0
- comments: 1.24.0
- contacts: 8.7.7
- contactsinteraction: 1.15.0
- dashboard: 7.14.0
- dav: 1.40.0
- deck: 1.18.3
- external: 9.0.1
- federatedfilesharing: 1.24.0
- federation: 1.24.0
- files: 2.6.0
- files_accesscontrol: 5.0.0
- files_automatedtagging: 5.0.0
- files_downloadlimit: 5.2.0
- files_external: 1.26.0
- files_lock: 34.0.1
- files_pdfviewer: 7.0.0-dev.0
- files_reminders: 1.7.0
- files_retention: 5.0.0
- files_sharing: 1.26.0
- files_trashbin: 1.24.0
- files_versions: 1.27.0
- flow_notifications: 5.0.0
- forms: 5.3.5
- guests: 4.9.0
- logcleaner: 1.6.3
- logreader: 7.0.0
- lookup_server_connector: 1.22.0
- nextcloud_announcements: 6.0.0
- notes: 6.0.2
- notifications: 7.0.0-dev.1
- notify_push: 1.4.0
- oauth2: 1.22.0
- onlyoffice: 10.1.2
- password_policy: 6.0.0-dev.0
- previewgenerator: 5.14.0
- privacy: 6.0.0-dev.1
- profile: 1.3.0
- provisioning_api: 1.24.0
- recommendations: 7.0.0
- related_resources: 5.0.0-dev.0
- serverinfo: 6.0.0
- settings: 1.17.0
- sharebymail: 1.24.0
- spreed: 24.0.4
- support: 6.0.0
- survey_client: 6.0.0-dev.0
- systemtags: 1.24.0
- tables: 2.3.0
- tasks: 0.18.1
- text: 8.0.0
- theming: 2.9.0
- thesearchpage: 2.4.5
- twofactor_backupcodes: 1.23.0
- twofactor_totp: 16.0.0
- updatenotification: 1.24.0
- user_status: 1.14.0
- viewer: 7.0.0-dev.0
- weather_status: 1.14.0
- webhook_listeners: 1.6.0
- workflow_script: 5.0.0
- workflowengine: 2.16.0
Disabled:
- app_api: 34.0.0 (installed 32.0.0)
- circles: 34.0.0 (installed 28.0.0)
- encryption: 2.22.0
- files_archive: 1.2.8 (installed 1.2.8)
- files_zip: 3.0.0 (installed 3.0.0)
- firstrunwizard: 7.0.0-dev.0 (installed 2.18.0)
- localpdf: 1.0.0 (installed 1.0.0)
- metadata: 0.24.0 (installed 0.24.0)
- office: 1.0.0 (installed 1.0.0)
- photos: 7.0.0 (installed 2.4.0)
- side_menu: 6.0.1 (installed 6.0.1)
- socialsharing_email: 4.1.0 (installed 4.1.0)
- socialsharing_telegram: 4.1.0 (installed 4.1.0)
- suspicious_login: 12.0.0-dev.0
- testing: 1.23.0
- twofactor_nextcloud_notification: 8.0.0
- user_ldap: 1.25.0
Nextcloud Signing status
Nextcloud Logs
### Nextcloud Logs
{"Exception":"OC\\DB\\Exceptions\\DbalException","Message":"An exception occurred while executing a query: SQLSTATE[22007]: Invalid datetime format: 1366 Incorrect string value: '\\xD0' for column `CloudDB`.`oc_systemtag`.`name` at row 1","Code":1366,"Trace":"#8 /var/www/html/nextcloud/lib/private/SystemTag/SystemTagManager.php(195): OC\\DB\\QueryBuilder\\QueryBuilder->executeStatement() #9 /var/www/html/nextcloud/apps/dav/lib/SystemTag/SystemTagPlugin.php(193): OC\\SystemTag\\SystemTagManager->createTag() #10 /var/www/html/nextcloud/apps/dav/lib/SystemTag/SystemTagPlugin.php(122): OCA\\DAV\\SystemTag\\SystemTagPlugin->createTag() #11 .../sabre/event/lib/WildcardEmitterTrait.php(89): OCA\\DAV\\SystemTag\\SystemTagPlugin->httpPost() ... #16 /var/www/html/nextcloud/remote.php(152)","CustomMessage":"Uncaught exception"}
Additional info
Additional info
- The misleading "Invalid datetime format" prefix is just Doctrine's generic
label for SQLSTATE class 22007; the actual error is MySQL 1366
(invalid string value).
- PostgreSQL and SQLite users are likely affected differently (PostgreSQL
will also reject invalid UTF-8; SQLite may silently store the broken
bytes), but I have only verified MariaDB.
- The silent-corruption variant (cut lands on a character boundary) means
instances that have had long multibyte tag names for a while probably
already contain mangled duplicate tags in oc_systemtag.
Bug description
OC\SystemTag\SystemTagManagertruncates tag names to 64 bytes using thebyte-based
substr($tagName, 0, 64). When the tag name contains multibyteUTF-8 characters (e.g. Cyrillic, 2 bytes per character) and the 64th byte
falls inside a character, the truncated string ends with a dangling lead
byte (e.g.
\xD0) — invalid UTF-8. MariaDB/MySQL in strict mode then rejectsthe INSERT with error 1366, which surfaces to the user as an HTTP 500 on
POST /remote.php/dav/systemtagsfrom the web UI.There are three affected call sites (line numbers from v34.0.3):
getTag()$truncatedTagName = substr($tagName, 0, 64);createTag()$truncatedTagName = substr($tagName, 0, 64);updateTag()$truncatedNewName = substr($newName, 0, 64);Two distinct user-visible consequences:
→ SQLSTATE 22007 / MySQL error 1366, unhandled, returned as 500. Any
Cyrillic tag name of 33+ characters can trigger this. Users see a generic
error and typically retry several times (visible in our access logs).
boundary, a mangled, mid-word-truncated tag is created without any warning
(real examples from our instance:
8 ерийalongside the intended8 серий;Для платформ, триллер, черная комед). This also createsnear-duplicate tags that fragment file/tag associations.
Root cause:
substr()counts bytes and cannot know about UTF-8 character boundaries.Since the limit here is a byte budget (the column is
varchar(64)),the correct function is
mb_strcut(), which enforces the same byte limitbut backs off to the nearest character boundary and never emits an invalid
sequence.
Note:
mb_substr()is not a correct replacement — it counts characters,so 64 Cyrillic characters would be 128 bytes and overflow the column.
Proposed fix:
Replace all three occurrences:
We have been running this patch in production since 2026-08-27: the 500 is
gone, over-long names are truncated at a clean character boundary, and
ASCII-only behavior is unchanged (
mb_strcutis byte-identical tosubstrfor single-byte input). Happy to submit this as a PR if the approach is
acceptable.
A possible follow-up (out of scope for this fix): silently truncating user
input at all is questionable — returning a proper 400 with a meaningful
message, or letting the WebDAV endpoint reject over-long names, might be
preferable UX. The
mb_strcutchange is the minimal correctness fix eitherway.
Steps to reproduce
oc_systemtagisutf8mb4_bin— charset configuration is not theissue, the payload is genuinely invalid UTF-8 after truncation).
Equivalently via the web UI: create a tag whose name is 63
acharactersfollowed by
б(or any Cyrillic name where character 33 starts atbyte 64, e.g. many real-world descriptive Cyrillic tag names).
Expected behavior
Tag is created with the name truncated at a character boundary (≤64 bytes,
no split character) — or, arguably better, the request is rejected with a
clear validation error. In no case a 500.
Actual behavior:
An exception occurred while executing a query:
SQLSTATE[22007]: Invalid datetime format: 1366
Incorrect string value: '\xD0' for column
<db>.oc_systemtag.nameat row 1List of activated Apps
Nextcloud Signing status
Nextcloud Logs
Additional info
Additional info
label for SQLSTATE class 22007; the actual error is MySQL 1366
(invalid string value).
will also reject invalid UTF-8; SQLite may silently store the broken
bytes), but I have only verified MariaDB.
instances that have had long multibyte tag names for a while probably
already contain mangled duplicate tags in
oc_systemtag.