DeviC3 opened a new issue, #13816:
URL: https://github.com/apache/cloudstack/issues/13816
### problem
Creating an instance from a backup (`createVMFromBackup` with
`startvm=true`) fails with a `NullPointerException` in the API response, even
though the instance is created correctly, its volumes are fully restored from
the backup and the VM boots and runs.
The user-visible effect is a red error in the UI with no indication that the
operation actually succeeded, and the generated password is not returned.
```
2026-08-05 18:37:06,819 ERROR [c.c.a.ApiAsyncJobDispatcher]
(API-Job-Executor-20:[ctx-f743d928, job-138]) (logid:5f51fca6) Unexpected
exception while executing
org.apache.cloudstack.api.command.admin.vm.CreateVMFromBackupCmdByAdmin
java.lang.NullPointerException: Cannot invoke "java.util.Map.get(Object)"
because the return value of "com.cloud.utils.Pair.second()" is null
at
com.cloud.vm.UserVmManagerImpl.startVirtualMachine(UserVmManagerImpl.java:5342)
at
com.cloud.vm.UserVmManagerImpl.restoreVMFromBackup(UserVmManagerImpl.java:9848)
at
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at
java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:569)
at
org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344)
at
org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198)
at
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at
org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)
at
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at
org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215)
at jdk.proxy3/jdk.proxy3.$Proxy202.restoreVMFromBackup(Unknown Source)
at
org.apache.cloudstack.api.command.user.vm.CreateVMFromBackupCmd.execute(CreateVMFromBackupCmd.java:124)
at com.cloud.api.ApiDispatcher.dispatch(ApiDispatcher.java:173)
at
com.cloud.api.ApiAsyncJobDispatcher.runJob(ApiAsyncJobDispatcher.java:110)
at
org.apache.cloudstack.framework.jobs.impl.AsyncJobManagerImpl$5.runInContext(AsyncJobManagerImpl.java:698)
...
2026-08-05 18:37:06,820 DEBUG [o.a.c.f.j.i.AsyncJobManagerImpl]
(API-Job-Executor-20:[ctx-f743d928, job-138]) (logid:5f51fca6) Complete async
job-138, jobStatus: FAILED, resultCode: 530, result:
{"uuidList":[],"errorcode":"530","errortext":"Cannot invoke
\"java.util.Map.get(Object)\" because the return value of
\"com.cloud.utils.Pair.second()\" is null"}
```
### Root cause
The trigger is a source VM whose template has `enable_password = 1`
("Password Enabled").
`restoreVMFromBackup` starts the VM **twice**:
1. First with `Param.ReturnAfterVolumePrepare = true` -
[UserVmManagerImpl.java#L9808](https://github.com/apache/cloudstack/blob/4.22.1.0/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java#L9808).
On this run `vm.isUpdateParameters()` is still `true`, so the params map is
built
([L5795](https://github.com/apache/cloudstack/blob/4.22.1.0/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java#L5794-L5795))
and the flag is then cleared - `vm.setUpdateParameters(false)` at
[L5843](https://github.com/apache/cloudstack/blob/4.22.1.0/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java#L5835-L5845).
2. Then, when `cmd.getStartVm()` is true, it performs the real start -
[UserVmManagerImpl.java#L9848](https://github.com/apache/cloudstack/blob/4.22.1.0/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java#L9848).
On the second run `vm.isUpdateParameters()` is already `false`, so `params`
stays `null` and the returned `Pair.second()` is `null`. The private
`startVirtualMachine` overload then dereferences it unconditionally whenever
the template has a password enabled -
[UserVmManagerImpl.java#L5339-L5342](https://github.com/apache/cloudstack/blob/4.22.1.0/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java#L5339-L5343):
```java
VMTemplateVO template =
_templateDao.findByIdIncludingRemoved(vm.getTemplateId());
if (template.isEnablePassword()) {
// this value is not being sent to the backend; need only for api
display purposes
vm.setPassword((String)vmParamPair.second().get(VirtualMachineProfile.Param.VmPassword));
}
```
The exception is thrown **outside** the `try` block at
[L9806-L9829](https://github.com/apache/cloudstack/blob/4.22.1.0/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java#L9806-L9829),
so the `expunge()` cleanup in the catch does not run. That is why the VM
survives in a correct, running state and only the API response fails - the
`VM.CREATE.FROM.BACKUP` event itself is recorded as `Completed`.
The same unguarded dereference is present on `main` -
[UserVmManagerImpl.java#L5597](https://github.com/apache/cloudstack/blob/main/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java#L5597).
### versions
- CloudStack **4.22.1.0** (packages `cloudstack-management-4.22.1.0-1`,
`cloudstack-agent-4.22.1.0-1`); code inspected on tag `4.22.1.0` and on `main`
- Hypervisor: **KVM** on AlmaLinux 9.8, `qemu-kvm 10.1.0`, `libvirt
11.10.0`, OpenJDK 17.0.20
- Primary storage: **Ceph RBD** (Ceph 20.2.2)
- Backup provider: **NAS Backup & Recovery**
(`backup.framework.provider.plugin = nas`), repository on an NFS export backed
by CephFS
- Database: MySQL 8.0.46
- 3 management servers, single advanced zone
### The steps to reproduce the bug
1. Register a template with **Password Enabled = true** (`enable_password =
1`).
2. Deploy an instance from that template.
3. Enable the backup framework with the `nas` provider, assign a backup
offering to the instance and take a backup.
4. Go to **Backups**, select the backup, choose **Create Instance from
Backup** and leave **Start Instance = Yes**.
5. The job fails with the NPE above (`errorcode 530`), but
`listVirtualMachines` shows the new instance in state `Running` with its
volumes correctly restored.
The bug does not appear when the template has Password Enabled unset, or
when **Start Instance = No** is selected.
### What to do about it?
Workaround for users: create the instance with **Start Instance = No** and
start it afterwards - the manual start path (`StartVMCmd`) only uses
`Pair.first()`, so it is unaffected.
Minimal fix - guard the dereference at `UserVmManagerImpl.java:5342`:
```java
if (template.isEnablePassword() && vmParamPair.second() != null) {
vm.setPassword((String)vmParamPair.second().get(VirtualMachineProfile.Param.VmPassword));
}
```
A cleaner fix would be for `restoreVMFromBackup` to reuse the params map
returned by the first `startVirtualMachine` call instead of letting the second
call re-derive it from a flag the first call has already consumed - that would
also make the generated password available in the API response, which is
arguably the intended behaviour.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]