Skip to content

Troubleshooting

For expected configuration and runtime status, also see Configuration reference, Scan your media library, and Media and thumbnails.

Thumbnails not showing / permission denied errors

Section titled “Thumbnails not showing / permission denied errors”

Symptoms:

  • Images load but thumbnails never appear
  • Docker logs contain EACCES: permission denied or mkdir ... EACCES (-13)
  • The cache folder, or a subfolder such as cache/thumbs, is owned by the wrong user

Cause:

PICR runs inside the container as UID 1000. If the cache folder on the host was created by Docker automatically (or by root), PICR cannot write thumbnails into it.

If your Compose file sets a custom user: "<uid>:<gid>", use that UID/GID instead of 1000:1000 for the cache folder. This is common when CAN_WRITE=true is enabled for a NAS media mount, because the container user often needs to match the NAS media owner. The cache volume must be writable by the same container user. For example, a container running as user: "1026:100" needs the host cache folder, including cache/thumbs, to be writable by 1026:100.

Fix:

Stop the container, fix ownership of the cache folder, then restart:

Reset cache ownership for the default container user
docker compose down
sudo chown -R 1000:1000 ./cache
docker compose up -d

For a custom container user, replace 1000:1000 with the UID/GID from your Compose file:

Reset cache ownership for a custom container user
docker compose down
sudo chown -R 1026:100 ./cache
sudo find ./cache -type d -exec chmod 775 {} +
sudo find ./cache -type f -exec chmod 664 {} +
docker compose up -d

Thumbnails will regenerate automatically once the container has write access.

You can verify cache write access from inside the running container:

Verify cache writes from inside PICR
docker compose exec picr sh -lc '
set -eu
id
stat -c "%n owner=%u:%g mode=%A" /home/node/app/cache /home/node/app/cache/thumbs
mkdir -p "/home/node/app/cache/thumbs/__picr_probe__/nested"
echo ok > "/home/node/app/cache/thumbs/__picr_probe__/nested/write-test.txt"
cat "/home/node/app/cache/thumbs/__picr_probe__/nested/write-test.txt"
rm -f "/home/node/app/cache/thumbs/__picr_probe__/nested/write-test.txt"
rmdir "/home/node/app/cache/thumbs/__picr_probe__/nested" "/home/node/app/cache/thumbs/__picr_probe__"
'

Prevention:

Create the cache (and data) folders yourself before the very first docker compose up, as described in Installation. If you let Docker create them it will do so as root.


If you deleted the cache folder and restarted, Docker will have recreated it as root — see the fix above.

The safe way to clear the thumbnail cache is to delete the contents of the folder, not the folder itself:

Clear cache contents, but keep the cache directory
rm -rf ./cache/*

A Bad Gateway error on GenerateZip means the request to start preparation failed. It does not establish that the archive itself exceeded a proxy timeout: ZIP creation runs in the background, and the browser downloads it after it is complete. Check the failing request and server logs before changing proxy limits.

  1. Record the PICR version from Settings, the exact failure time and timezone, and whether you use a reverse proxy or tunnel. Try the same download through PICR’s direct address on your local network and compare the result.

  2. In browser developer tools, open Network, reproduce once, and inspect the failed request. Record its URL path, HTTP status, response Content-Type, and elapsed time. Note whether it is /graphql (GenerateZip), task polling, /zip/..., or an individual /image/.../raw/... download.

  3. If a 50 MB original downloads as only a few KB, inspect that small file as text. It may contain an HTML error page rather than RAW bytes; its size alone does not identify the cause.

  4. Collect container status and recent logs. Replace picr below if your Compose service has a different name:

    Collect container status and recent logs
    docker compose ps
    docker compose logs --since 15m --timestamps picr
    docker compose exec picr tail -n 100 /home/node/app/cache/error.log
    docker compose exec picr tail -n 100 /home/node/app/cache/info.log

    Look for ZIP failures, ENOSPC/quota errors, EACCES, ENOENT, RAW decode failures, and signs of a container restart. PICR’s operational logs are in the cache files, so Docker logs alone may not contain the cause.

  5. Check free bytes and inodes on the cache filesystem:

    Check available ZIP cache space
    docker compose exec picr df -h /home/node/app/cache
    docker compose exec picr df -i /home/node/app/cache

    A gallery of 94 files at 50–60 MB each contains approximately 4.7–5.6 GB of source data. Allow comparable additional cache space for the ZIP, plus a margin and space already occupied by thumbnails and other archives. Compression savings depend on the source files.

  6. Compare one individual download, a small test folder containing a few of the same RAW files, and the full gallery. If previews are missing too, include the RAW decode messages from error.log. Preview failure must not prevent an original download in versions containing the ZIP delivery fix.

Send these results with a bug report. Remove public-link UUIDs, passcodes, authorization headers, and private paths before sharing screenshots or logs. Capture evidence before restarting or clearing caches.

After upgrading to the ZIP delivery fix, previously cached ZIPs are regenerated on the next download because older versions could leave incomplete archives. Legacy ZIPs remain on disk; the upgrade does not delete them or generate ZIPs for the whole library.


If PICR starts but immediately exits with a database connection error, the most common cause is another Postgres instance already using port 5432 on your host.

Both Synology DSM and DaVinci Resolve run their own Postgres servers on 5432 by default.

Fix — expose the container on a different host port in compose.yml:

compose.yml — optional PostgreSQL host port
db:
ports:
- '54321:5432'

This does not affect PICR itself (it connects to Postgres inside Docker using the internal port), but it frees up 5432 on the host for other tools.


PICR says database migrations failed on first start

Section titled “PICR says database migrations failed on first start”

PICR retries its startup migrations once after 10 seconds if Postgres is not ready yet. If it still fails, check:

  1. The db container is running and healthy (docker compose ps)
  2. Your compose.yml includes the healthcheck and depends_on.condition: service_healthy as shown in Installation
  3. The DATABASE_URL environment variable matches the credentials in the db service

If the admin Server Info page shows “CPU only” when you expected VAAPI to be detected, work through these. The reason shown on that page (and in the startup log) tells you which step failed.

1. Are you on the right image? VAAPI drivers ship in the amd64 image only. On arm64 PICR always uses the CPU.

2. Is the render device inside the container?

Check for a render device
docker compose exec picr ls -l /dev/dri

You should see renderD128 (or similar). If it’s missing, add the device mapping to your picr service:

compose.yml — pass through the render device
devices:
- /dev/dri:/dev/dri

3. Does the container user have permission? PICR runs as node, which must be in the host’s render group. Check the host GID and add it under group_add:

Find the host render-group ID
getent group render # e.g. render:x:992: -> use 992
compose.yml — grant render-group access
group_add:
- '992'

A permission problem usually shows up as a probe failure mentioning the device can’t be opened.

4. Can the GPU actually be used? Inspect VAAPI support from inside the container:

Probe VAAPI inside PICR
docker compose exec picr vainfo --display drm --device /dev/dri/renderD128

A healthy GPU prints a driver line and a list of VAProfile… entries. Errors here point at host drivers rather than PICR.

Common “CPU only” reasons:

  • /dev/dri/renderD128 is not available — no device mapped in, or wrong path (set VIDEO_ACCELERATION_DEVICE for a non-default node).
  • VAAPI probe failed: … — the device exists but VAAPI couldn’t initialise, usually a permission (render group) or host-driver issue.
  • video acceleration is disabled (VIDEO_ACCELERATION=off) — you (or your compose) set VIDEO_ACCELERATION=off.

Almost always this means PICR is being served over plain HTTP on a LAN address, such as http://192.168.1.50:6900/. Browsers only expose the clipboard API on a secure context — HTTPS, or localhost. On a plain-HTTP LAN address the API simply isn’t there.

PICR falls back to an older copy method in that situation, so the button should still work. If it doesn’t:

  1. Copy the URL shown under the link ID in the link editor manually — it is the complete link.
  2. Put PICR behind HTTPS. See Publish PICR with HTTPS.

Serving PICR over HTTPS is the real fix, and avoids the same class of problem in other browser features.

Check the GitHub Issues — search before opening a new one in case it has already been reported or resolved.