Guide
A Comprehensive Agent's Guide to Laravel CloudReads that lie
· Ed Grosvenor
An agent decides what to do from what it reads. On Cloud, several reads are reliably wrong in ways that look like real answers: an empty array, a running status, a clean log window. Treat absence as unproven until a read you trust confirms it.
Relationship fields that come back empty
These were empty or null while the resource was attached and in use:
| Read | Field | Use instead |
|---|---|---|
application:list |
Each environment's database, cache and WebSocket IDs | environment:get per environment |
cache:list, cache:get |
environmentIds |
environment:get per environment, reading cacheId |
websocket-cluster:list |
applicationIds |
websocket-application:list <cluster-id> |
bucket:list |
keyIds |
bucket-key:list <bucket-id> |
bucket:list |
No environment field at all | Nothing. No read links a bucket to an environment. |
environment:get is the read we trust for attachments. It returns databaseSchemaId, cacheId and websocketApplicationId for that environment. Even so, we have notes of it under-reporting a cache on one version and reporting correctly on another, so cross-check anything you are about to act on.
The API has its own version of this: without an include, responses have no relationships key at all. A missing key is not the same as an empty relationship.
Identifiers that do not produce "not found"
- Getters for a deleted or unknown ID have returned
Multiple applications foundwith a list of options, or exited with status1, rather than reporting that the thing does not exist. The application resolver can also quietly substitute another application. See Work in the right organization. - A
geton a deleted WebSocket application returned an unrelated "multiple clusters found" error.
Prove that something is gone by its absence from a fresh, complete listing, never by an error from a get.
Listings that are incomplete or duplicated
- Listings have under-counted. One read returned 10 deployments where there were 46. From v0.6.2, the CLI follows pages up to a cap of 2,000 rows. If you are on an older version, or calling the API yourself, follow
links.nextuntil it isnull. database-cluster:listreturned duplicate rows in one audit. De-duplicate by ID before counting.- Billing is not an inventory. An application deleted halfway through a billing period still appears in usage, and an application with no spend is absent from it.
Statuses that mean less than they say
status: runningwhile asleep. A hibernating environment reportsrunning. The only signal is the replica count:cloud environment:metrics <environment-id> --period=6h --json -nfrom v0.6.3, orGET /api/environments/{id}/metrics, wherereplica_countis0while it sleeps.command.successmeans the command was delivered. ReadexitCode. See Run commands on Cloud.build.succeededis not the end of a deployment. Wait fordeployment.succeeded.- A green deployment proves the health check on
/passed, nothing more. A queue worker can be crash-looping behind it. - Exit code
0on a write is not proof the write happened.background-process:deletehas exited0without deleting anything, andenvironment:update --database-iddid the same on v0.5.0. Re-read after every write. - The hibernation flag flip-flops. After
instance:update --scale-to-zero, theusesHibernationread changed between reads for us. Re-read hibernation settings after an update before relying on them.
Logs that are partial
cloud environment:logs <application-id> <environment-id> --from=2026-10-01T10:00:00Z --to=2026-10-01T10:05:00Z --json -n
- Every call returns at most 100 entries, the last 100 in the window.
--taildoes not raise it. On a busy application, 100 entries can cover only a few seconds, so a wide window silently shows you its final moments. Sample narrow windows. If a window returns fewer than 100 entries, it is complete. If it returns exactly 100, it is not. - Retention is about three days. An older window returns an
Unprocessableerror, not an empty list. - "No errors in the logs" is a measurement only if the window was complete.
- Scheduler output does not appear in
environment:logs. GET /api/environments/{id}/logs?from=…&to=…returns system lines as well, including container starts. It is the only way we found to see a restart without touching the application.- The full build and deploy log for a deployment is only at
GET /api/deployments/{id}/logs.
Configuration reads
environment:getlists only the variables you set. Cloud-injected values never appear. Ask the running application instead.cloud usagedoes not print the organization's ID. It reports usage for whichever organization the CLI resolved.
The API specification
The published specification has been wrong about which values an endpoint accepts, and it drifts without notice:
- Its enums list the values that exist, not the ones valid for a particular resource or plan. Constraints live in prose descriptions, and some of those are wrong.
- Relationships get renamed. The database cluster relationship
schemasbecamedatabases.include=schemasstarted returning a400while the specification still listed it, and code still reading the old key created a duplicate database on a cluster that was already billing. - On 28 September 2026, database types changed from a single value to a family plus a required
version.
If you build against the API, check the specification for drift on a schedule, and when a live response and the specification disagree, believe the response.
Rate limits
Loops that call the API once per resource get rate-limited. One audit was throttled after ten database clusters. A rate-limited check is inconclusive, not a pass: slow down, re-run what was throttled, and only then draw a conclusion.