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.
$result = $client->getNodes()->get('pve01')->getQemu()->get(100)->getStatus()->getStart()->vmStart();
if (!$result->isSuccessStatusCode()) { echo "Start failed: {$result->getStatusCode()} {$result->getReasonPhrase()}";}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:
$result = $client->set('/nodes/pve01/qemu/100/config', ['memory' => 'abc']);
if (!$result->isSuccessStatusCode()) { echo $result->getReasonPhrase() . "\n"; // Parameter verification failed. if ($result->responseInError()) { echo $result->getError() . "\n"; // 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 error of curl, for example Could not resolve host: pve99 |
| Request timed out (the timeout of the client) | 0 | The error of curl, for example Connection timed out after 3001 milliseconds |
| An answer that is not JSON (a proxy page, another service on that port) | The status received | The reason received |
In all three getResponse() is null.
Behind a reverse proxy that speaks HTTP/2 the reason is empty, because HTTP/2 has none: the status code is still there.
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, getNodeFromTask |
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 |
create, set and the generated methods that use them |
InvalidArgumentException |
A parameter cannot be written as JSON, for example text that is not UTF-8. Nothing is sent |
Both exceptions of the library carry the failed call in getResult().
Reading a member that is not in the answer, or the data of a call without an answer, does not
throw: PHP raises a warning and the value is null. See
Results.
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 function of its own:
use Corsinvest\ProxmoxVE\Api\PveResultException;use Corsinvest\ProxmoxVE\Api\Result;
function ensureSuccess(Result $result){ if (!$result->isSuccessStatusCode()) { throw new PveResultException($result, $result->responseInError() ? $result->getError() : "{$result->getStatusCode()} {$result->getReasonPhrase()}"); } if ($result->getResponse() === null) { throw new PveResultException($result, 'The answer is not from the Proxmox VE API'); } return $result;}$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 = null;
for ($attempt = 1; $attempt <= 3; $attempt++) { $result = $client->getCluster()->getResources()->resources();
$status = $result->getStatusCode(); $canPass = $status == 0 || $status >= 500; if ($result->isSuccessStatusCode() || !$canPass) { break; }
sleep($attempt * 2);}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.