Errors
The methods of the client do not throw when a call fails. A refused request, a missing privilege, a
node that cannot be reached: each returns a Result, and the code decides what to do.
var result = client.getNodes().get("pve01").getQemu().get(100).getStatus().getStart().vmStart();
if (!result.isSuccessStatusCode()) { System.out.println("Start failed: " + result.getStatusCode() + " " + result.getReasonPhrase()); return;}A call whose Result is not checked fails silently: the code goes on as if the VM had started.
What to check
Section titled “What to check”| Check | True when |
|---|---|
isSuccessStatusCode() |
The HTTP status is 200: Proxmox VE accepted the request |
responseInError() |
The answer has an errors member: one or more parameters were refused. The status is 400 |
getError() |
Text of those errors, one parameter per line. Empty when responseInError() is false |
getReasonPhrase() |
The reason of any failure: Proxmox VE writes its error message here |
isSuccessStatusCode() is the one to test: it is false for every failure, including those that
have responseInError(). Use getError() to show which parameter was wrong:
import java.util.Map;
var result = client.set("/nodes/pve01/qemu/100/config", Map.of("memory", "abc"));
if (!result.isSuccessStatusCode()) { System.out.println(result.getReasonPhrase()); // Parameter verification failed. if (result.responseInError()) { System.out.println(result.getError()); // memory : invalid format - ... }}Failures that never reached Proxmox VE
Section titled “Failures that never reached Proxmox VE”A request that cannot be completed is also returned as a Result, built by the client, so the same
check covers it:
| Failure | getStatusCode() |
getReasonPhrase() |
|---|---|---|
| Name not resolved, connection refused, certificate refused | 0 | The class and the message of the exception |
| Request timed out (the timeout of the client) | 408 | Request timed out after <n> ms: and the message of the exception |
| An answer that is not JSON (a proxy page, another service on that port) | 502, or the status received if it was already an error | The answer is not JSON (HTTP 200): and the start of the body |
In all three getData() is null. The exception of a request without an answer is written to the
log at SEVERE level: see Troubleshooting.
Status 500 is what Proxmox VE itself returns when it cannot do what was asked, for example for a VM
id that does not exist: the reason is in getReasonPhrase().
What does throw
Section titled “What does throw”| Call | Exception | When |
|---|---|---|
waitForTaskToFinish, taskIsRunning, getExitStatusTask |
PveResultException |
The status of the task cannot be read, or the value is not a UPID |
login |
PveExceptionAuthentication |
The user needs a second factor and none was given |
setTimeout |
IllegalArgumentException |
The timeout is negative |
Reading getData().get("member") |
NullPointerException |
The call failed, or the member is not in the answer |
PveResultException is unchecked and carries the failed call in getResult().
PveExceptionAuthentication is checked, so every call to login has to handle or declare it.
Throw on every failure
Section titled “Throw on every failure”An application that prefers exceptions can turn every failed call into one with a small method of its own:
import it.corsinvest.proxmoxve.api.PveResultException;import it.corsinvest.proxmoxve.api.Result;
static Result ensureSuccess(Result result) { if (!result.isSuccessStatusCode()) { throw new PveResultException(result, result.responseInError() ? result.getError() : result.getStatusCode() + " " + result.getReasonPhrase()); } return result;}var result = ensureSuccess(client.getNodes().get("pve01").getQemu().get(100).getStatus().getStart().vmStart());A successful call is not a finished operation
Section titled “A successful call is not a finished operation”For the operations that run as a task, a successful Result means the task was started. Whether it
ended well is in the exit status of the task: see Tasks.
Retrying
Section titled “Retrying”Because failures are results, a retry is a loop on the Result, not a catch. Retry only what is
safe to repeat, and only for failures that can pass:
Result result = null;
for (var attempt = 1; attempt <= 3; attempt++) { result = client.getCluster().getResources().resources();
var status = result.getStatusCode(); var canPass = status == 0 || status == 408 || status >= 500; if (result.isSuccessStatusCode() || !canPass) { break; }
Thread.sleep(attempt * 2000L);}The other statuses (wrong parameters, no permission) give the same answer every time: retrying them is useless. A 401 from an expired ticket needs a new login, not a retry.