Skip to content

[Bug]: Nextcloud Photos shows blank/gradient previews for some JPEG files although the original image and server-side preview generation work correctly #2135

Description

@mrliuchang-crypto

Title:
Nextcloud 34.0.3 Photos preview API returns a corrupted/gradient preview for one JPEG while the original image is valid

Hello,

I am experiencing a very strange preview/thumbnail problem after upgrading to Nextcloud 34.0.3.

The original JPEG file is completely valid and can be opened normally. One image generated by the same phone works correctly, while another image from the same phone produces a preview that is essentially just a smooth/blurred gradient with no recognizable image content.

The problem is especially strange because:

  • The original JPEG can be decoded successfully by both ImageMagick and PHP GD.
  • PHP GD can resize the original image correctly.
  • Nextcloud reports "preview generated".
  • The preview database entries exist.
  • Some preview files are generated correctly.
  • The browser requests a 256x256 preview through the Photos API and receives HTTP 200.
  • However, the actual image displayed by the browser is not the real photo; it looks like a smooth gradient.
  • The problem is reproducible for one specific JPEG.
  • Another JPEG from the same phone works correctly.

Environment

Nextcloud Server:
34.0.3

Photos:
7.0.0

Preview Generator:
5.14.0

Memories:
8.1.0

OS:
Debian

Hardware:
Raspberry Pi 5, 8 GB RAM

Storage:
1 TB NVMe SSD

Web server:
Apache 2.4.68

PHP:
PHP 8.5.8

PHP extensions:

  • exif
  • gd
  • imagick

Database:
MariaDB/MySQL

Redis:
127.0.0.1:6379

Nextcloud data directory:
/mnt/pool/nextcloud_data

Appdata:
/mnt/pool/nextcloud_data/appdata_ocfy6fwmnciv

Preview directory:
/mnt/pool/nextcloud_data/appdata_ocfy6fwmnciv/preview

The instance is behind Cloudflare.

The public URL is:

https://nextcloud.circuitwater.com

The problem also occurs when accessing Nextcloud directly through the LAN IP:

http://192.168.0.102

The problem is therefore not caused by Cloudflare.


Two test images

I have two JPEG files from the same phone.

Working image:

26-08-21 07-52-38 9007.jpg

Broken image:

26-08-21 07-59-21 9008.jpg

Their Nextcloud file IDs are:

9007 -> fileid 879145
9008 -> fileid 879144

Database query:

SELECT fileid, path, mimetype, size, mtime
FROM oc_filecache
WHERE path LIKE '%26-08-21 07-52-38 9007.jpg'
OR path LIKE '%26-08-21 07-59-21 9008.jpg';

Result:

879144 | files/Photos/2026/08/26-08-21 07-59-21 9008.jpg | 8 | 3763725 | 1787313561
879145 | files/Photos/2026/08/26 07-52-38 9007.jpg | 8 | 8891981 | 1787313264

The two images were taken with different phone cameras:

  • 9007 is from the rear camera.
  • 9008 is from the front camera.

The important point is that the original 9008 JPEG itself is valid.


Original JPEG verification

Original file:

/mnt/pool/nextcloud_data/admin/files/Photos/2026/08/26-08-21 07-59-21 9008.jpg

ImageMagick:

sudo identify
'/mnt/pool/nextcloud_data/admin/files/Photos/2026/08-21 07-59-21 9008.jpg'

Result:

JPEG 3088x2316
8-bit sRGB
approximately 3.59 MiB

More detailed information:

Format: JPEG
Geometry: 3088x2316+0+0
Colorspace: sRGB
Compression: JPEG
Quality: 100
Orientation: RightTop

There is no JPEG decoding error.


PHP GD verification

PHP has:

exif
gd
imagick

PHP CLI version:

PHP 8.5.8

I tested the exact original file using PHP GD:

$im = imagecreatefromjpeg($f);

Result:

GD DECODE OK: 3088x2316

I then generated a resized JPEG directly with PHP GD:

192x256

Result:

GD RESIZE OK

The generated file was:

JPEG 192x256
9539 bytes

This resized image is normal.

Therefore:

  • JPEG is valid.
  • PHP GD can decode it.
  • PHP GD can resize it.
  • ImageMagick can decode it.
  • The problem does not appear to be a corrupt source JPEG.

Nextcloud preview database

For fileid 879144, the preview records are:

SELECT file_id,width,height,size,max,cropped,etag
FROM oc_previews
WHERE file_id=879144
ORDER BY width,height;

Result:

file_id width height size max cropped etag
879144 48 64 1534 0 0 c808473c0228963909100f77b11886fc
879144 192 256 9184 0 0 c808473c0228963909100f77b11886fc
879144 256 256 11001 0 1 c808473c0228963909100f77b11886fc
879144 256 341 14777 0 0 c808473c0228963909100f77b11886fc
879144 768 1024 102863 0 0 c808473c0228963909100f77b11886fc
879144 2316 3088 849744 1 0 c808473c0228963909100f77b11886fc


Preview files on disk

Preview directory for fileid 879144:

/mnt/pool/nextcloud_data/appdata_ocfy6fwmnciv/preview/9/6/c/2/8/f/9/879144/

The generated files include:

2316-3088-max.jpg
849744 bytes

48-64.jpg
1534 bytes

256-256-crop.jpg
11001 bytes

768-1024.jpg
102863 bytes

256-341.jpg
14777 bytes

The database and filesystem therefore contain matching preview records/files for several sizes.

However, the browser does not display the expected photo when requesting the preview through the Photos API.


Important browser request

When the browser loads the image, it requests:

/index.php/apps/photos/api/v1/preview/879144?etag=c808473c0228963909100f77b11886fc&x=256&y=256

The browser receives:

HTTP/1.1 200 OK

Content-Type:
image/jpeg

Content-Disposition:
inline; filename="192-256.jpg"

ETag:
"c808473c0228963909100f77b11886fc"

X-User-ID:
admin

So the request is authenticated and Nextcloud believes that it successfully returned a preview.

The important part is that the browser receives HTTP 200 and an image/jpeg response, but the displayed image is essentially a smooth gradient / blurred image with no recognizable photo content.


Direct LAN access

The problem is also reproducible without Cloudflare.

For example:

http://192.168.0.102/index.php/apps/photos/api/v1/preview/879144?etag=c808473c0228963909100f77b11886fc&x=256&y=256

This request returns HTTP 200 when authenticated from the browser.

The browser's request information shows:

Request URL:
http://192.168.0.102/index.php/apps/photos/api/v1/preview/879144?etag=c808473c0228963909100f77b11886fc&x=256&y=256

Request method:
GET

Status:
200 OK

Remote address:
192.168.0.102:80

Content-Type:
image/jpeg

Content-Disposition:
inline; filename="192-256.jpg"

X-User-ID:
admin

Therefore Cloudflare is not required to reproduce the problem.


Authentication/API investigation

A direct unauthenticated curl request returns:

HTTP/1.1 401 Unauthorized

{"message":"Current user is not logged in"}

This is expected because the API requires authentication.

When accessed from the browser, the same endpoint returns HTTP 200 and X-User-ID: admin.

Therefore this is not simply an authentication problem.


Proxy / overwrite configuration

Nextcloud configuration:

overwrite.cli.url = https://nextcloud.circuitwater.com

From the LAN:

overwrite.cli.url='https://nextcloud.circuitwater.com'
overwritehost=NULL
overwriteprotocol=NULL
overwritecondaddr=NULL

Through HTTPS:

overwrite.cli.url='https://nextcloud.circuitwater.com'
overwritehost='nextcloud.circuitwater.com'
overwriteprotocol='https'
overwritecondaddr=NULL

Apache is listening directly on:

*:80
*:443

There are no ProxyPass/ProxyPassReverse rules involved.

I also searched Apache configuration for:

ProxyPass
ProxyPassReverse
X-Forwarded
RemoteIP
SSLProxy
RequestHeader
SetEnvIf
Cloudflare

There is no relevant reverse proxy configuration.


Preview generation test

I ran:

sudo -u www-data php /var/www/nextcloud/occ preview:generate 879144

Output:

preview generated

I then removed all previews for this file and also removed the corresponding database records:

DELETE FROM oc_previews
WHERE file_id = 879144;

Confirmed:

preview_count = 0

Then:

sudo -u www-data php /var/www/nextcloud/occ preview:generate 879144

Again:

preview generated

After that, only these files initially appeared:

2316-3088-max.jpg
849744 bytes

48-64.jpg
1534 bytes

Later, after the Photos application requested previews, additional previews appeared, such as:

256-256-crop.jpg
11001 bytes

768-1024.jpg
102863 bytes

256-341.jpg
14777 bytes

This is particularly interesting because the preview generation command itself reports success, but the preview requested by the Photos API still does not correspond to the expected image.


Manual image generation works

I manually generated a resized image from the original file using ImageMagick:

convert
'/mnt/pool/nextcloud_data/admin/files/Photos/2026/08/26-08-21 07-59-21 9008.jpg'
-auto-orient
-resize '256x256>'
/tmp/9008-im.jpg

Result:

JPEG 192x256

This manually generated image is normal.

I also generated a resized image using PHP GD:

GD RESIZE OK

Result:

JPEG 192x256

Again, the result is normal.

This strongly suggests that the JPEG itself and the underlying image libraries are not the problem.


Preview Generator configuration

Preview Generator:

5.14.0

Configuration:

heightSizes = 256 384
squareSizes = 32 256
widthSizes = 256 384 512

Full configuration:

{
"apps": {
"previewgenerator": {
"enabled": "yes",
"heightSizes": "256 384",
"installed_version": "5.14.0",
"sizes": "{"square":[32,256],"width":[256,384,512],"height":[256,384]}",
"squareSizes": "32 256",
"types": "filesystem",
"widthSizes": "256 384 512"
}
}
}

The previewgenerator app is enabled.


Other Nextcloud preview configuration

Enabled preview providers include:

OC\Preview\Movie
OC\Preview\MP4
OC\Preview\MKV
OC\Preview\AVI
OC\Preview\Image
OC\Preview\HEIC

enable_previews = true

preview_ffmpeg_path = /usr/bin/ffmpeg

PHP memory_limit from CLI:

memory_limit=-1

No relevant preview_max / preview_memory / preview_filesize / preview_concurrency settings are configured.


Permissions

The preview directory and parents are owned by www-data:

appdata_ocfy6fwmnciv:
www-data:www-data

preview:
www-data:www-data

The complete path was checked using:

namei -l

All relevant preview directories are accessible by www-data.

For example:

/mnt/pool/nextcloud_data
www-data:www-data
drwxr-x---

/mnt/pool/nextcloud_data/appdata_ocfy6fwmnciv
www-data:www-data
drwxr-xr-x

/mnt/pool/nextcloud_data/appdata_ocfy6fwmnciv/preview
www-data:www-data
drwxr-xr-x

The generated preview directories are also:

www-data:www-data

Therefore this does not appear to be a simple permissions problem.


Important observation about preview:generate-all

I previously ran:

occ preview:generate-all

This produced additional preview records/files and may explain why some preview sizes exist that are not directly explained by the current Preview Generator configuration.

Therefore, please do not assume that every existing preview size was generated by the current previewgenerator configuration.

I subsequently deleted the previews and database entries for fileid 879144 and tested:

occ preview:generate 879144

The problem still occurs.


Comparison with the working image

The working image is:

fileid 879145
26-08-21 07-52-38 9007.jpg

It has:

original size: 8,891,981 bytes

Maximum preview:

3024x4032
2,156,040 bytes

The problematic image:

fileid 879144
26-08-21 07-59-21 9008.jpg

Original size:

3,763,725 bytes

Original dimensions:

3088x2316

Maximum preview:

2316x3088
849,744 bytes

Both files are valid JPEGs.

Both are from the same phone.

The main difference is that 9007 was taken with the rear camera and 9008 with the front camera.


What I expected

When requesting:

/index.php/apps/photos/api/v1/preview/879144?etag=...&x=256&y=256

I expect Nextcloud to return a normal thumbnail of the original JPEG.


What actually happens

The API returns HTTP 200 and image/jpeg.

The browser displays an image that is essentially a smooth gradient / completely blurred image with no recognizable content.

The original file itself is perfectly viewable.

Manually resizing the original file with ImageMagick or PHP GD produces a perfectly normal image.

So the issue appears to be somewhere between:

original JPEG
->
Nextcloud preview generation/storage
->
Photos preview API
->
browser

rather than the JPEG decoder itself.


Additional observation

The browser sometimes appears to display the gradient image even when the corresponding expected preview file is not obvious in the preview directory.

For example, the browser requested:

x=256
y=256

but the response had:

Content-Disposition: inline; filename="192-256.jpg"

There was no file named:

192-256.jpg

in the fileid 879144 preview directory at the time I checked.

This makes me suspect that the Photos preview API may be generating or returning an on-demand preview that is not necessarily the same file as the pre-generated preview files managed by previewgenerator.

I would appreciate confirmation of exactly how the Photos app resolves this preview request and whether the new preview database/storage implementation in Nextcloud 34 could be involved.


Possible relation to recent Nextcloud 34 preview issues

I noticed that there are already recent reports concerning preview generation and the new preview database implementation in Nextcloud 34.0.3, including SQL errors involving PreviewMapper and oc_previews / preview_versions.

My instance does not currently show the same SQL error for this particular file, but the timing and preview behavior make me wonder whether this is related to the same area of code.


What I have already ruled out

I have tested/verified:

  • Original JPEG is valid.
  • ImageMagick can decode the original.
  • PHP GD can decode the original.
  • PHP GD can resize the original correctly.
  • ImageMagick can resize the original correctly.
  • PHP has GD and Imagick installed.
  • PHP memory_limit is unlimited in CLI.
  • Preview directory permissions are correct.
  • www-data owns the preview directory.
  • Nextcloud recognizes the file correctly as JPEG.
  • The original file is accessible from the expected data path.
  • The issue occurs using the LAN IP directly.
  • The issue does not require Cloudflare.
  • The API returns 200 when accessed through the authenticated browser session.
  • The browser request includes X-User-ID: admin.
  • Deleting the preview files and oc_previews records does not fix the problem.
  • Running occ preview:generate for the specific file does not fix the problem.
  • The problem is specific to at least this JPEG and is not simply "all previews are missing".

Request

Could someone please help determine where the incorrect image is being generated/returned?

In particular, I would like to know:

  1. Which component generates the image returned by:

    /index.php/apps/photos/api/v1/preview/?etag=...&x=256&y=256

  2. Why does the API return a valid JPEG response (HTTP 200) but the actual content is only a smooth gradient?

  3. Why can PHP GD and ImageMagick correctly resize the original JPEG, while Nextcloud's preview path produces the incorrect result?

  4. Is this related to the preview changes in Nextcloud 34 / the new oc_previews / preview_versions implementation?

  5. Could the Photos 7.0.0 preview API be requesting a preview specification that is being incorrectly resolved?

  6. Is there a way to enable more detailed logging specifically around PreviewManager / PreviewMapper / Photos preview API so that I can identify exactly which preview file or generator is producing the bad image?

  7. If this is a known regression in Nextcloud 34.0.3, is upgrading to a newer 34.x release recommended, or should I downgrade to Nextcloud 33 temporarily?

I can provide additional logs, database information, or test results if needed.

Thank you.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions