Ubuntu: "Could not make loader for libvips"
I'm getting the error message
Exception in thread "main" java.lang.IllegalStateException: java.lang.UnsatisfiedLinkError: could not make loader for libvips
on startup. This happens in an Ubuntu 24.04 host environment (no Docker) with libvips-dev installed.
The library is present at
/usr/lib/x86_64-linux-gnu/libvips.so.42
/usr/lib/x86_64-linux-gnu/libvips.so.42.17.1
and a call to vips --version results in the output
vips-8.15.1
If I add the parameter
-Dvipsffm.libpath.vips.override="/usr/lib/x86_64-linux-gnu/libvips.so.42"
then I get the following error message:
java.lang.IllegalArgumentException: path override requested for vips, but library not found at path: /usr/lib/x86_64-linux-gnu/libvips.so.42
What am I missing?
Had a look, and struggling to figure out the root cause. I added an "Ubuntu 24.04" test to the Docker suite, and that's working OK. Whilst investigating I realised the error message is a little misleading (it's not necessarily just about the path, and could be a permissions/architecture thing. Plus the cause was missing from the stack trace).
I've released 1.5.2 with the improved error message (it should be up on Maven Central in ~20 minutes), and it should include the underlying cause of the IllegalArgumentException in the log output. Could you give it a try, and share the underlying cause too please?
I have just put 1.5.2 to test and got the same error message. This is the whole stacktrace:
java.lang.UnsatisfiedLinkError: could not make loader for libvips
at app.photofox.vipsffm.VipsLibLookup.buildSymbolLoader(VipsLibLookup.java:14) ~[vips-ffm-core-1.5.2.jar:na]
at app.photofox.vipsffm.jextract.VipsRaw.<clinit>(VipsRaw.java:59) ~[vips-ffm-core-1.5.2.jar:na]
at app.photofox.vipsffm.VipsHelper.init(VipsHelper.java:19) ~[vips-ffm-core-1.5.2.jar:na]
at app.photofox.vipsffm.Vips.init(Vips.java:17) ~[vips-ffm-core-1.5.2.jar:na]
at app.photofox.vipsffm.Vips.init(Vips.java:12) ~[vips-ffm-core-1.5.2.jar:na]
at com.test.springdemo.demo.DemoApplication.lambda$commandLineRunner$0(DemoApplication.java:29) ~[classes/:na]
at org.springframework.boot.SpringApplication.lambda$callRunner$5(SpringApplication.java:788) ~[spring-boot-3.4.3.jar:3.4.3]
at org.springframework.util.function.ThrowingConsumer$1.acceptWithException(ThrowingConsumer.java:82) ~[spring-core-6.2.3.jar:6.2.3]
at org.springframework.util.function.ThrowingConsumer.accept(ThrowingConsumer.java:60) ~[spring-core-6.2.3.jar:6.2.3]
at org.springframework.util.function.ThrowingConsumer$1.accept(ThrowingConsumer.java:86) ~[spring-core-6.2.3.jar:6.2.3]
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:796) ~[spring-boot-3.4.3.jar:3.4.3]
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:787) ~[spring-boot-3.4.3.jar:3.4.3]
at org.springframework.boot.SpringApplication.lambda$callRunners$3(SpringApplication.java:772) ~[spring-boot-3.4.3.jar:3.4.3]
at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184) ~[na:na]
at java.base/java.util.stream.SortedOps$SizedRefSortingSink.end(SortedOps.java:357) ~[na:na]
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:571) ~[na:na]
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:560) ~[na:na]
at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151) ~[na:na]
at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174) ~[na:na]
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:265) ~[na:na]
at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:636) ~[na:na]
at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:772) ~[spring-boot-3.4.3.jar:3.4.3]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:325) ~[spring-boot-3.4.3.jar:3.4.3]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1361) ~[spring-boot-3.4.3.jar:3.4.3]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1350) ~[spring-boot-3.4.3.jar:3.4.3]
at com.test.springdemo.demo.DemoApplication.main(DemoApplication.java:21) ~[classes/:na]
What can I do to help you investigate this issue?
@fischermario Could you do the same thing you did before, manually setting the vipsffm.libpath.vips.override option, using 1.5.2 please? I'm hoping the improved error logging includes something more explicit about what's wrong.
I'm so sorry! After adding -Dvipsffm.libpath.vips.override="/usr/lib/x86_64-linux-gnu/libvips.so.42" I got this error message:
java.lang.ExceptionInInitializerError: null
at app.photofox.vipsffm.VipsHelper.init(VipsHelper.java:19) ~[vips-ffm-core-1.5.2.jar:na]
at app.photofox.vipsffm.Vips.init(Vips.java:17) ~[vips-ffm-core-1.5.2.jar:na]
at app.photofox.vipsffm.Vips.init(Vips.java:12) ~[vips-ffm-core-1.5.2.jar:na]
at com.test.springdemo.demo.DemoApplication.lambda$0(DemoApplication.java:29) ~[classes/:na]
at org.springframework.boot.SpringApplication.lambda$callRunner$5(SpringApplication.java:788) ~[spring-boot-3.4.3.jar:3.4.3]
at org.springframework.util.function.ThrowingConsumer$1.acceptWithException(ThrowingConsumer.java:82) ~[spring-core-6.2.3.jar:6.2.3]
at org.springframework.util.function.ThrowingConsumer.accept(ThrowingConsumer.java:60) ~[spring-core-6.2.3.jar:6.2.3]
at org.springframework.util.function.ThrowingConsumer$1.accept(ThrowingConsumer.java:86) ~[spring-core-6.2.3.jar:6.2.3]
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:796) ~[spring-boot-3.4.3.jar:3.4.3]
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:787) ~[spring-boot-3.4.3.jar:3.4.3]
at org.springframework.boot.SpringApplication.lambda$callRunners$3(SpringApplication.java:772) ~[spring-boot-3.4.3.jar:3.4.3]
at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184) ~[na:na]
at java.base/java.util.stream.SortedOps$SizedRefSortingSink.end(SortedOps.java:357) ~[na:na]
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:571) ~[na:na]
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:560) ~[na:na]
at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151) ~[na:na]
at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174) ~[na:na]
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:265) ~[na:na]
at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:636) ~[na:na]
at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:772) ~[spring-boot-3.4.3.jar:3.4.3]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:325) ~[spring-boot-3.4.3.jar:3.4.3]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1361) ~[spring-boot-3.4.3.jar:3.4.3]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1350) ~[spring-boot-3.4.3.jar:3.4.3]
at com.test.springdemo.demo.DemoApplication.main(DemoApplication.java:21) ~[classes/:na]
Caused by: java.lang.IllegalArgumentException: path override requested for vips, but library not valid at path: /usr/lib/x86_64-linux-gnu/libvips.so.42
at app.photofox.vipsffm.VipsLibLookup.makeLibraryAtPathNotValidException(VipsLibLookup.java:116) ~[vips-ffm-core-1.5.2.jar:na]
at app.photofox.vipsffm.VipsLibLookup.lambda$makeOptionalLibraryLookup$0(VipsLibLookup.java:104) ~[vips-ffm-core-1.5.2.jar:na]
at java.base/java.util.Optional.map(Optional.java:260) ~[na:na]
at app.photofox.vipsffm.VipsLibLookup.makeOptionalLibraryLookup(VipsLibLookup.java:99) ~[vips-ffm-core-1.5.2.jar:na]
at app.photofox.vipsffm.VipsLibLookup.findVipsLoader(VipsLibLookup.java:29) ~[vips-ffm-core-1.5.2.jar:na]
at app.photofox.vipsffm.VipsLibLookup.buildSymbolLoader(VipsLibLookup.java:12) ~[vips-ffm-core-1.5.2.jar:na]
at app.photofox.vipsffm.jextract.VipsRaw.<clinit>(VipsRaw.java:59) ~[vips-ffm-core-1.5.2.jar:na]
... 24 common frames omitted
Caused by: java.lang.IllegalArgumentException: Cannot open library: /usr/lib/x86_64-linux-gnu/libvips.so.42
at java.base/java.lang.foreign.SymbolLookup.libraryLookup(SymbolLookup.java:348) ~[na:na]
at java.base/java.lang.foreign.SymbolLookup.libraryLookup(SymbolLookup.java:333) ~[na:na]
at app.photofox.vipsffm.VipsLibLookup.lambda$makeOptionalLibraryLookup$0(VipsLibLookup.java:102) ~[vips-ffm-core-1.5.2.jar:na]
... 29 common frames omitted
This is now interesting:
path override requested for vips, but library not valid at path: /usr/lib/x86_64-linux-gnu/libvips.so.42
What criteria makes this library not valid?
There is something very interesting though: if I provide an invalid path I will get the same error message (library not valid at path). Is this a problem caused by incorrect permissions?
Damn, the underlying error is just "Cannot open library" from SymbolLookup.libraryLookup 🤔
Out of curiosity, what does ls -l /usr/lib/x86_64-linux-gnu/libvips.so.42 show for you? My best guess would be a permission issue (where the JVM somehow doesn't have permission to read the library file).
This is the output of the ls command:
$ ls -l /usr/lib/x86_64-linux-gnu/libvips.so.42
lrwxrwxrwx 1 root root 18 Apr 1 2024 /usr/lib/x86_64-linux-gnu/libvips.so.42 -> libvips.so.42.17.1
The error even occurs if I run the jvm as root.
Those permissions look sensible to me. Is it possible for you to share the entire command used to run the application? I'm also curious what the LD_LIBRARY_PATH environment variable looks like for you.
Though, I still don't understand why the load would fail when you explicitly set the path.
And a couple more sense checks:
- What does
ls -l /usr/lib/x86_64-linux-gnu/libvips.so.42.17.1look like - does the symlinked file definitely exist/have the right permissions? - Does the same error happen if you use that path explicitly? (not sure why a symlink would break Java's loader, but worth trying)
I set LD_LIBRARY_PATH explicitly like this:
export LD_LIBRARY_PATH="/usr/lib/x86_64-linux-gnu"
This is the output of ls -l /usr/lib/x86_64-linux-gnu/libvips.so.42.17.1:
-rw-r--r-- 1 root root 2745632 Apr 1 2024 /usr/lib/x86_64-linux-gnu/libvips.so.42.17.1
Using this path produces the same error.
I have set up a demo project here.
The project is a spring boot app that I run by invoking the Maven wrapper like this
./mvnw -X spring-boot:run
You will find the additional JVM parameters inside the pom.xml file.
Interestingly I get a different error message when running this project. I'm still overwriting the vips path, but now it complains about glib:
java.lang.UnsatisfiedLinkError: could not make loader for glib
at app.photofox.vipsffm.VipsLibLookup.buildSymbolLoader(VipsLibLookup.java:18) ~[vips-ffm-core-1.5.2.jar:na]
at app.photofox.vipsffm.jextract.VipsRaw.<clinit>(VipsRaw.java:59) ~[vips-ffm-core-1.5.2.jar:na]
at app.photofox.vipsffm.VipsHelper.init(VipsHelper.java:19) ~[vips-ffm-core-1.5.2.jar:na]
at app.photofox.vipsffm.Vips.init(Vips.java:17) ~[vips-ffm-core-1.5.2.jar:na]
at app.photofox.vipsffm.Vips.init(Vips.java:12) ~[vips-ffm-core-1.5.2.jar:na]
at com.test.demo.DemoApplication.lambda$commandLineRunner$0(DemoApplication.java:22) ~[classes/:na]
at org.springframework.boot.SpringApplication.lambda$callRunner$5(SpringApplication.java:788) ~[spring-boot-3.4.3.jar:3.4.3]
at org.springframework.util.function.ThrowingConsumer$1.acceptWithException(ThrowingConsumer.java:82) ~[spring-core-6.2.3.jar:6.2.3]
at org.springframework.util.function.ThrowingConsumer.accept(ThrowingConsumer.java:60) ~[spring-core-6.2.3.jar:6.2.3]
at org.springframework.util.function.ThrowingConsumer$1.accept(ThrowingConsumer.java:86) ~[spring-core-6.2.3.jar:6.2.3]
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:796) ~[spring-boot-3.4.3.jar:3.4.3]
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:787) ~[spring-boot-3.4.3.jar:3.4.3]
at org.springframework.boot.SpringApplication.lambda$callRunners$3(SpringApplication.java:772) ~[spring-boot-3.4.3.jar:3.4.3]
at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184) ~[na:na]
at java.base/java.util.stream.SortedOps$SizedRefSortingSink.end(SortedOps.java:357) ~[na:na]
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:571) ~[na:na]
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:560) ~[na:na]
at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151) ~[na:na]
at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174) ~[na:na]
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:265) ~[na:na]
at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:636) ~[na:na]
at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:772) ~[spring-boot-3.4.3.jar:3.4.3]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:325) ~[spring-boot-3.4.3.jar:3.4.3]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1361) ~[spring-boot-3.4.3.jar:3.4.3]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1350) ~[spring-boot-3.4.3.jar:3.4.3]
at com.test.demo.DemoApplication.main(DemoApplication.java:16) ~[classes/:na]
Manually providing the path to glib results in a similar error like before.
Ran out of time today but will take a look as time permits after work this week. The glib error suggests that it did successfully load libvips, and glib was missing, but I'll give it a try on macOS first and then maybe an Ubuntu VM.
I have updated the demo project in the repository. It now also contains a README file.
The project works if I provide the paths to all three libraries (vips, glib, gobject). As of now it all boils down to a problem inside the library detection logic.
How can I help you to investigate further?
Could you give this command a try please - I found it mentioned for debugging where libraries are linked from: https://github.com/libvips/libvips/issues/712#issuecomment-322719454
ldd which vips | grep libvips
I'm interested where the vips CLI thinks it's loading libraries from. Maybe there's a pattern I missed or should add. But I'm glad that specifying them manually works at least.
Just to sense check, on macOS with a couple of tweaks to the run configuration, this worked OK:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<environmentVariables>
<!-- <LD_LIBRARY_PATH>/usr/lib/x86_64-linux-gnu</LD_LIBRARY_PATH>-->
<DYLD_LIBRARY_PATH>/opt/homebrew/lib</DYLD_LIBRARY_PATH>
</environmentVariables>
<jvmArguments>--enable-native-access=ALL-UNNAMED</jvmArguments>
</configuration>
</plugin>
You could also try this configuration, commenting out the DYLD_LIBRARY_PATH option (which is macOS specific) and uncommenting the LD_LIBRARY_PATH option, then trying to run it.
@lopcode
I'm interested where the vips CLI thinks it's loading libraries from. Maybe there's a pattern I missed or should add.
Of course! I got this output:
$ ldd `which vips` | grep libvips
libvips.so.42 => /lib/x86_64-linux-gnu/libvips.so.42 (0x0000781466600000)
@lopcode
You could also try this configuration, commenting out the
DYLD_LIBRARY_PATHoption (which is macOS specific) and uncommenting theLD_LIBRARY_PATHoption, then trying to run it.
I have tried these two variants:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<environmentVariables>
<LD_LIBRARY_PATH>/lib/x86_64-linux-gnu</LD_LIBRARY_PATH>
</environmentVariables>
<jvmArguments>--enable-native-access=ALL-UNNAMED</jvmArguments>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<environmentVariables>
<LD_LIBRARY_PATH>/usr/lib/x86_64-linux-gnu</LD_LIBRARY_PATH>
</environmentVariables>
<jvmArguments>--enable-native-access=ALL-UNNAMED</jvmArguments>
</configuration>
</plugin>
Both end up with the error message java.lang.UnsatisfiedLinkError: could not make loader for libvips 😞
Is there something else that I can try?
@fischermario Sorry this is proving tricky to debug.
Could you share the output of:
-
ls -al /usr/lib/x86_64-linux-gnu | grep vips -
ls -al /usr/lib/x86_64-linux-gnu | grep glib-2.0 -
ls -al /usr/lib/x86_64-linux-gnu | grep gobject-2.0
And, what specific OS version are you using?
@lopcode
Done. Here is the output:
ls -al /usr/lib/x86_64-linux-gnu | grep vips
lrwxrwxrwx 1 root root 22 Apr 1 2024 libvips-cpp.so.42 -> libvips-cpp.so.42.17.1
-rw-r--r-- 1 root root 268616 Apr 1 2024 libvips-cpp.so.42.17.1
lrwxrwxrwx 1 root root 18 Apr 1 2024 libvips.so.42 -> libvips.so.42.17.1
-rw-r--r-- 1 root root 2745632 Apr 1 2024 libvips.so.42.17.1
drwxr-xr-x 2 root root 4096 Mär 16 13:49 vips-modules-8.15
ls -al /usr/lib/x86_64-linux-gnu | grep glib-2.0
drwxr-xr-x 2 root root 4096 Mär 16 13:45 glib-2.0
lrwxrwxrwx 1 root root 23 Nov 13 18:42 libglib-2.0.so.0 -> libglib-2.0.so.0.8000.0
-rw-r--r-- 1 root root 1343056 Nov 13 18:42 libglib-2.0.so.0.8000.0
ls -al /usr/lib/x86_64-linux-gnu | grep gobject-2.0
lrwxrwxrwx 1 root root 26 Nov 13 18:42 libgobject-2.0.so.0 -> libgobject-2.0.so.0.8000.0
-rw-r--r-- 1 root root 399752 Nov 13 18:42 libgobject-2.0.so.0.8000.0
And, what specific OS version are you using?
$ lsb_release -a
Distributor ID: Ubuntu
Description: Ubuntu 24.04.2 LTS
Release: 24.04
Codename: noble
Thank you for your patience. I hope that this helps 😊
Still not sure what could be wrong here. I am wondering if ./mvnw is stripping out library locations or something (like macOS does), but that wouldn't explain why the library fails to be found automatically when you explicitly set LD_LIBRARY_PATH.
I might play around with a Docker container and the sample project, but haven't been able to reproduce any issues so far. At least you can work around the problem by setting library paths manually.
Doing some more sense checking - I made a Dockerfile based on Ubuntu 24.04, that downloads a JDK, installs libvips, and runs the demo project without any extra options - the libraries are discovered automatically. So, I think something might be different about your specific environment?
If you want to give it a try yourself it's on this branch
From that branch, I ran:
-
docker build --progress=plain -t ubuntu-lib-demo . -
docker run -it ubuntu-lib-demo
getting same error with this Dockerfile
FROM bellsoft/liberica-runtime-container:jdk-22-stream-musl AS builder
WORKDIR /app
COPY . .
RUN chmod +x mvnw && ./mvnw clean package -DskipTests
FROM bellsoft/liberica-openjre-debian:22
RUN apt-get update -y && \
apt-get install -y --no-install-recommends \
libvips \
libwebp-dev && \
rm -rf /var/lib/apt/lists/*
COPY --from=builder /app/target/media-service.jar app.jar
EXPOSE 8088
ENV JAVA_TOOL_OPTIONS="--enable-native-access=ALL-UNNAMED"
ENTRYPOINT ["java", "-jar", "/app.jar"]
Hi @HasanHaghniya - the correct dependency that includes all the libraries needed is libvips-dev (rather than just libvips).
There is however a second problem - it seems the libvips version available for that particular image is quite old - 8.10.5. vips-ffm has trouble with a few things on old versions (like block_untrusted_set doesn't exist, and streaming operations fail).
I'd recommend using a newer version of the JVM base image, if possible. The vips-ffm tests include a sample for Debian 12.1, for example: https://github.com/lopcode/vips-ffm/blob/main/docker_tests/debian-12/Dockerfile - that has libvips 8.14.1 available, which is thoroughly tested with.
I'll close this for now - if folks still have issues, feel free to ping me.