Python Wand: MagickReadImage returns false, but did not raise ImageMagick exception
I've got some long-standing code in a Django code base that reads in a PDF and uses Wand to take a screenshot of the first page of the PDF, which is then displayed on the website. We recently migrated servers (an upgrade from Ubuntu 22 LTS to 24 LTS), and something broke, and I can't for the life of me figure it out.
First, some potentially useful information:
- OS: Ubuntu 24 LTS
- Python 3.12.3
- Django 5.2.4
- Wand 0.6.13
- Web server: nginx 1.24.0
- gunicorn version: 23.0.0
- We are not using Docker. This Django app is running directly on the server with a local virtual environment.
The PDF-to-PNG code is on the admin side of the web app. Here's the heart of it:
with Image(filename=pdf_location) as pdf:
with Image(pdf.sequence[0]) as first_page_pdf:
with first_page_pdf.convert('png') as first_page_png:
first_page_png.background_color = Color('white')
first_page_png.alpha_channel = 'remove'
return first_page_png.make_blob()
When I upload a PDF to the admin site for processing, I'm getting this error:
MagickReadImage returns false, but did not raise ImageMagick exception. This can occur when a delegate is missing, or returns EXIT_SUCCESS without generating a raster.
I have tried everything I can think of after a ton of searching, but nothing is working:
- I do have ghostscript installed:
$ gs --version
10.02.1
$ which gs
/usr/bin/gs
- My ImageMagick
policy.xml
contains the default content found in thepolicy-debian.xml
file that's included with the ImageMagick package, with the notable exception of ensuring that<policy domain="coder" rights="read|write" pattern="PDF" />
is in thepolicy.xml
. I can verify that the PDF policy is properly set:
$ identify -list policy
Path: /etc/ImageMagick-6/policy.xml
Policy: Resource
name: disk
value: 2GiB
Policy: Resource
name: map
value: 2048MiB
Policy: Resource
name: memory
value: 1024MiB
Policy: Resource
name: area
value: 256MP
Policy: Resource
name: height
value: 32KP
Policy: Resource
name: width
value: 32KP
Policy: Undefined
rights: None
Policy: Path
rights: None
pattern: @*
Policy: Delegate
rights: None
pattern: URL
Policy: Delegate
rights: None
pattern: HTTPS
Policy: Delegate
rights: None
pattern: HTTP
Policy: Coder
rights: Read Write
pattern: PDF
Path: [built-in]
Policy: Undefined
rights: None
- Conversion of the same PDF file does work when I do it manually with both ghostscript and imagemagick (as suggested here):
$ gs -sDEVICE=pngalpha -o page-%03d.png -r120 pdf-test.pdf
GPL Ghostscript 10.02.1 (2023-11-01)
Copyright (C) 2023 Artifex Software, Inc. All rights reserved.
This software is supplied under the GNU AGPLv3 and comes with NO WARRANTY:
see the file COPYING for details.
Processing pages 1 through 1.
Page 1
Loading font ArialMT (or substitute) from /usr/share/ghostscript/10.02.1/Resource/Font/NimbusSans-Regular
and
$ convert -density 120 pdf-test.pdf page-%03d.png
Both correctly create page-001.png
when using this test PDF.
- And finally, when doing this manually within my Django shell (i.e., within the same venv that nginx uses), it also works properly:
$ ./manage_dev.py shell
19 objects imported automatically (use -v 2 for details).
Python 3.12.3 (main, Jun 18 2025, 17:59:45) [GCC 13.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from wand.image import Image, Color
>>> with Image(filename='pdf-test.pdf') as pdf:
... with Image(pdf.sequence[0]) as first_page_pdf:
... with first_page_pdf.convert('png') as first_page_png:
... first_page_png.background_color = Color('white')
... first_page_png.alpha_channel = 'remove'
... blob = first_page_png.make_blob()
... with open('screenshot.png', 'wb') as png:
... png.write(blob)
...
24420
- One thing that is a bit bizarre is that when I list the ImageMagick delegates,
gs
is not listed. This is the only thing I can think of that might be causing the issue, but I can't figure out how to get it listed:
$ convert -list configure | grep DELEGATES
DELEGATES bzlib djvu fftw fontconfig freetype heic jbig jng jpeg lcms lqr lzma openexr openjp2 pango png ps raw tiff webp wmf x xml zlib zstd
DELEGATES bzlib djvu fftw fontconfig freetype heic jbig jng jp2 jpeg lcms lqr ltdl lzma openexr pangocairo png raw tiff webp wmf x xml zlib
Note that this is not a typo; there are 2 DELEGATES
lines here, and neither contains gs
.
To reiterate, this code has worked perfectly for many years prior to this server migration/upgrade, so all this leads me to believe that it must be some configuration file (ImageMagick, nginx?) somewhere outside of my code, but I just can't nail it down. I'm really hoping that one of you might have some insights.
Thanks in advance!