Skip to content

Screenshot mirror returns an empty body for a quarter of all apps #26

Description

@nursoda

apps.json hands clients raw screenshot URLs; a Nextcloud instance never fetches those
itself but asks the mirror at
https://usercontent.apps.nextcloud.com/<base64 of the URL as it appears in apps.json>.
For a large share of apps that mirror answers HTTP 200 with an empty body, while the
source URL serves the image fine. Instances therefore show no screenshot in the app list.
The store website is unaffected — it embeds the source URL directly.

Steps to reproduce

curl -sL --compressed -o apps.json https://apps.nextcloud.com/api/v1/platform/34.0.0/apps.json
jq -r '.[] | .id as $i | .screenshots[]? | $i + "\t" + (.url|@base64)' apps.json |
while IFS=$'\t' read -r id b64; do
  n=$(curl -s "https://usercontent.apps.nextcloud.com/$b64" | head -c 20 | wc -c)
  [ "$n" -eq 0 ] && echo "empty: $id"
done

The base64 key must be built from the URL string exactly as it appears in apps.json,
including any surrounding whitespace — several apps write <screenshot> across multiple
lines in info.xml, and that whitespace is part of the key. (Whitespace itself is not the
bug: 80 of the 88 whitespace-carrying URLs are mirrored correctly.)

Expected behaviour

The mirror serves the image, as it does for the majority of apps.

Actual behaviour

Measured 2026-09-09 against /api/v1/platform/34.0.0/apps.json:

result screenshot URLs
image delivered 763
empty body (0 bytes) 318
body File not found (14 bytes) 5
total 1086 (from 381 apps)

97 of 381 apps with screenshots have at least one empty image.
Examples: moviedb, crate, n8n_sync, homecheck, maintenancecheck, twofactor_email,
folder_protection, kanso, integration_pexip, agora, llmchat, launchpad.

A random sample of ten affected URLs was fetched from their source: all ten returned
HTTP 200 with the full image (94–630 KB). So the source is not the problem.

Guess

A first fetch that failed appears to be cached as an empty entry and never retried.
Re-fetching on an empty cache entry, or not caching failures at all, would fix it.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions