⚠️ This issue respects the following points: ⚠️
Bug description
Text does not render Markdown images whose source is a data: URI, and — more seriously — once
such a document is opened and edited, the images are removed from the stored file. They are not
shown as broken, or as a placeholder: the image is absent from the document Text builds, so the
next save writes the file back without it. A one-word text edit is enough to destroy every picture
in the document.
Inline data: images are valid Markdown (CommonMark places any URL in ), and they are
the natural format for self-contained documents — a single file that stays complete when it is
moved, copied, shared, or read offline. Several tools export chats, notes and reports this way.
Steps to reproduce
- Create a Markdown file containing one inline image, e.g.
 — a normal JPEG, nothing exotic.
- Upload it to Nextcloud (WebDAV or the Files web UI). Confirm the file on the server still
contains the data: URI.
- Open it in Files. Text displays the document — the image is missing, with no indication
that anything was omitted.
- Change one word of the text and let Text save.
- Download the file, or inspect it on the server.
Expected behavior
Either the image renders, or — at minimum — content Text cannot represent is preserved on save.
Silently discarding part of a user's file is the part that matters: the document was complete when
it was opened and is not when it is closed.
Actual behaviour
The image is gone from the stored file.
Measured on a live instance (sizes and MD5s of the same document before and after):
| copy |
size |
md5 |
| as uploaded |
56344 |
ad73141d3179f82d35e97bf482fe23b7 |
| after opening in Text and editing one line |
2888 |
97f7f445021d9b82fbaa4ff015ae3757 |
The  line is not altered or replaced — it is deleted. Nextcloud's
own file versioning kept the original, which is the only reason nothing was lost; a user without
versions enabled, or one who notices after the retention window, loses the images permanently.
Where it comes from
Text builds image nodes from markdown-it tokens via the image node's parseMarkdown, and the
TipTap Image extension it uses declares:
addOptions() { return { inline: false, allowBase64: false, HTMLAttributes: {}, resize: false } }
…
parseHTML() { return [{ tag: this.options.allowBase64 ? "img[src]" : 'img[src]:not([src^="data:"])' }] }
allowBase64 is TipTap's own default and Text never sets it, so data: sources are excluded. The
node is therefore never created; the document Text holds has no image in it; and the Markdown
serializer, which writes what the document contains, emits a file without the image.
Worth noting that every other layer already accepts these images:
- markdown-it, which Text bundles, explicitly permits them — its own validator is
/^data:image\/(gif|png|jpeg|webp);/, i.e. raster images pass and data:image/svg+xml does not.
- Nextcloud's CSP already allows
data: images (img-src * data: blob:).
So the exclusion is not a considered security position — it is an unset option, inherited from a
library default, that happens to also delete user data.
Suggested fix
- Never drop unrepresentable content on save. Whatever is decided about rendering, a document
that arrives with content Text cannot display should not be written back without it. This is the
data-loss half and it is independent of the rest.
- Allow inert raster data URIs. Enable base64 images for
data:image/png, image/jpeg,
image/gif, image/webp and keep rejecting data:image/svg+xml, which is the only genuinely
dangerous case (SVG can carry script). markdown-it's existing allow-list is exactly this set, so
the two layers would finally agree.
- If inline images are unwanted as a matter of product design, then say so in the UI — a
placeholder reading "1 image not displayed" would at least warn the user before they edit and
lose it.
Workaround (for anyone finding this issue)
Removing the guard from the built chunk restores both rendering and round-trip safety:
:not([src^="data:"]) → (removed, 3 occurrences)
in apps/text/js/Wrapper-<hash>.chunk.mjs. It is wiped by every update, it fails the code
integrity check, and it allows SVG data URIs too — so it is a stopgap, not a fix.
Nextcloud Server version
34
Operating system
Debian/Ubuntu
PHP engine version
PHP 8.3
Web server
Nginx
Database engine version
MariaDB
Is this bug present after an update or on a fresh install?
Fresh Nextcloud Server install
Are you using the Nextcloud Server Encryption module?
None
What user-backends are you using?
Configuration report
List of activated Apps
### Steps to reproduce
1. Create a Markdown file containing one inline image, e.g.
`` — a normal JPEG, nothing exotic.
2. Upload it to Nextcloud (WebDAV or the Files web UI). Confirm the file on the server still
contains the `data:` URI.
3. Open it in Files. Text displays the document — **the image is missing**, with no indication
that anything was omitted.
4. Change one word of the text and let Text save.
5. Download the file, or inspect it on the server.
### Expected behaviour
Either the image renders, or — at minimum — content Text cannot represent is **preserved on save**.
Silently discarding part of a user's file is the part that matters: the document was complete when
it was opened and is not when it is closed.
### Actual behaviour
The image is gone from the stored file.
Measured on a live instance (sizes and MD5s of the same document before and after):
| copy | size | md5 |
|---|---|---|
| as uploaded | 56344 | `ad73141d3179f82d35e97bf482fe23b7` |
| after opening in Text and editing one line | **2888** | `97f7f445021d9b82fbaa4ff015ae3757` |
The `` line is not altered or replaced — it is deleted. Nextcloud's
own file versioning kept the original, which is the only reason nothing was lost; a user without
versions enabled, or one who notices after the retention window, loses the images permanently.
### Where it comes from
Text builds image nodes from markdown-it tokens via the image node's `parseMarkdown`, and the
TipTap `Image` extension it uses declares:
addOptions() { return { inline: false, allowBase64: false, HTMLAttributes: {}, resize: false } }
…
parseHTML() { return [{ tag: this.options.allowBase64 ? "img[src]" : 'img[src]:not([src^="data:"])' }] }
`allowBase64` is TipTap's own default and Text never sets it, so `data:` sources are excluded. The
node is therefore never created; the document Text holds has no image in it; and the Markdown
serializer, which writes what the document contains, emits a file without the image.
Worth noting that every *other* layer already accepts these images:
- **markdown-it**, which Text bundles, explicitly permits them — its own validator is
`/^data:image\/(gif|png|jpeg|webp);/`, i.e. raster images pass and `data:image/svg+xml` does not.
- **Nextcloud's CSP** already allows `data:` images (`img-src * data: blob:`).
So the exclusion is not a considered security position — it is an unset option, inherited from a
library default, that happens to also delete user data.
### Suggested fix
1. **Never drop unrepresentable content on save.** Whatever is decided about rendering, a document
that arrives with content Text cannot display should not be written back without it. This is the
data-loss half and it is independent of the rest.
2. **Allow inert raster data URIs.** Enable base64 images for `data:image/png`, `image/jpeg`,
`image/gif`, `image/webp` and keep rejecting `data:image/svg+xml`, which is the only genuinely
dangerous case (SVG can carry script). markdown-it's existing allow-list is exactly this set, so
the two layers would finally agree.
3. If inline images are unwanted as a matter of product design, then **say so in the UI** — a
placeholder reading "1 image not displayed" would at least warn the user before they edit and
lose it.
### Workaround (for anyone finding this issue)
Removing the guard from the built chunk restores both rendering and round-trip safety:
:not([src^="data:"]) → (removed, 3 occurrences)
in `apps/text/js/Wrapper-<hash>.chunk.mjs`. It is wiped by every update, it fails the code
integrity check, and it allows SVG data URIs too — so it is a stopgap, not a fix.
### Server configuration
- Nextcloud: **34.0.3**
- Text app: **7.0.1**
- PHP: 8.3.6
- OS: Ubuntu 24.04 LTS
- Web server: nginx
- Database: MariaDB 10.11
### Client
Chromium-based browser, desktop. Not browser-specific — the content never reaches the DOM.
Nextcloud Signing status
Nextcloud Logs
Additional info
No response
Bug description
Text does not render Markdown images whose source is a
data:URI, and — more seriously — oncesuch a document is opened and edited, the images are removed from the stored file. They are not
shown as broken, or as a placeholder: the image is absent from the document Text builds, so the
next save writes the file back without it. A one-word text edit is enough to destroy every picture
in the document.
Inline
data:images are valid Markdown (CommonMark places any URL in), and they arethe natural format for self-contained documents — a single file that stays complete when it is
moved, copied, shared, or read offline. Several tools export chats, notes and reports this way.
Steps to reproduce
— a normal JPEG, nothing exotic.contains the
data:URI.that anything was omitted.
Expected behavior
Either the image renders, or — at minimum — content Text cannot represent is preserved on save.
Silently discarding part of a user's file is the part that matters: the document was complete when
it was opened and is not when it is closed.
Actual behaviour
The image is gone from the stored file.
Measured on a live instance (sizes and MD5s of the same document before and after):
ad73141d3179f82d35e97bf482fe23b797f7f445021d9b82fbaa4ff015ae3757The
line is not altered or replaced — it is deleted. Nextcloud'sown file versioning kept the original, which is the only reason nothing was lost; a user without
versions enabled, or one who notices after the retention window, loses the images permanently.
Where it comes from
Text builds image nodes from markdown-it tokens via the image node's
parseMarkdown, and theTipTap
Imageextension it uses declares:allowBase64is TipTap's own default and Text never sets it, sodata:sources are excluded. Thenode is therefore never created; the document Text holds has no image in it; and the Markdown
serializer, which writes what the document contains, emits a file without the image.
Worth noting that every other layer already accepts these images:
/^data:image\/(gif|png|jpeg|webp);/, i.e. raster images pass anddata:image/svg+xmldoes not.data:images (img-src * data: blob:).So the exclusion is not a considered security position — it is an unset option, inherited from a
library default, that happens to also delete user data.
Suggested fix
that arrives with content Text cannot display should not be written back without it. This is the
data-loss half and it is independent of the rest.
data:image/png,image/jpeg,image/gif,image/webpand keep rejectingdata:image/svg+xml, which is the only genuinelydangerous case (SVG can carry script). markdown-it's existing allow-list is exactly this set, so
the two layers would finally agree.
placeholder reading "1 image not displayed" would at least warn the user before they edit and
lose it.
Workaround (for anyone finding this issue)
Removing the guard from the built chunk restores both rendering and round-trip safety:
in
apps/text/js/Wrapper-<hash>.chunk.mjs. It is wiped by every update, it fails the codeintegrity check, and it allows SVG data URIs too — so it is a stopgap, not a fix.
Nextcloud Server version
34
Operating system
Debian/Ubuntu
PHP engine version
PHP 8.3
Web server
Nginx
Database engine version
MariaDB
Is this bug present after an update or on a fresh install?
Fresh Nextcloud Server install
Are you using the Nextcloud Server Encryption module?
None
What user-backends are you using?
Configuration report
List of activated Apps
Nextcloud Signing status
Nextcloud Logs
Additional info
No response