Troubleshooting
Validating a manifest before you ship it, and debugging one that isn't deploying.
Validating your manifest
Before shipping, check a manifest against the rules on Tasks:
- Schema — PascalCase keys,
Type: Deploy/V2, a non-emptyActionsmap (or the legacy top-levelTasks, never both), no two action keys naming the same action - Identity —
ReleaseIdequals the release you'll deploy it in - Per-task — each
Execute/v2supplies its method's input (ExeFilefor MSI/RPM/DEB/Script,Targetfor API,ExeFile/Targetfor*_Uninstall); eachVerification/v2has a verify method (API/Script/SSE, and aMessagefor SSE); eachRevert/v2has a removal method; eachDeploy/v2/FleetDeploy/v2sets anActionthat exists (in this manifest when it targets itself); eachFleetDeploy/v2has aRule; eachGroup/v2is non-empty; anyRulehas at least one condition - Ordering — every
Verification/Reverthas a target (a precedingExecutesibling or an actionable parent); every rebooting task has a nestedVerification - Artifacts — every
ExeFileis actually included in the release; everyDeploy/v2target is a registered dependency, delivered (Done+ artifacts on disk), and itself a V2 component - Placeholders — every
{Source.Path}uses a known source and resolves to a value that fits its slot on the target device (a scalar in text; a whole{Config.*}group only as aBody/Messagevalue)
At runtime the agent enforces all of these; a failure appears in
<logs_dir>/deployments/<catalog_id>.log with the failing stage or task index.
Symptoms
| Symptom | Likely cause | Where to look |
|---|---|---|
Deploy Error immediately, no task ran | Validation failed (parse/structure/main-release/version) | Deploy log — the Validation of action '…' failed at stage '…' line |
Action Error with "Deploy V2 start rejected for …" in the log | The start was refused before running (release not delivered, no install.yaml, …) | The reason after the colon on that log line |
| "manifest ReleaseID … does not match requested deploy …" | ReleaseId ≠ the deployed release | Manifest ReleaseId vs. the release id |
| "Agent version … does not meet minimum required …" | Agent older than MinAgentVersion | Manifest MinAgentVersion; upgrade agent |
| "artifact not found for …" | An ExeFile wasn't delivered | Release contents vs. task ExeFile |
| "dependent … is not a registered dependency of …" | The Deploy/v2 target isn't declared as a dependency of the parent in the catalog | The release's registered dependencies vs. the task ReleaseId |
| "delivery … has no artifacts" / "artifact … not found on disk" / "Item downloading not complete" | A release in the tree isn't fully delivered (status and state Done) or a file is missing | The delivery status/state and the on-disk artifacts |
| "dependent … is not a Deploy V2 component (no install.yaml)" | A dependency isn't V2 | Dependency release must ship install.yaml |
| "dependency cycle detected" / "action cycle detected" / "dependency depth exceeded" | A release + action that (directly or indirectly) runs itself, or too-deep nesting | The Deploy/v2 chain; DEPLOY_MAX_DEPENDENCY_DEPTH |
"Cannot resolve argument placeholder: {…}" | Unknown source, missing key, missing config group, or a non-scalar in a text slot | The placeholder and its source; for {Config.*}, the device's synced config groups |
| "… resolved to a JSON object/array, expected a scalar value" | A whole {Config.<group>} (or an object) used in an argument, target, header or param | Use a group + key path, {ConfigJson.*}, or move it to a Body value |
A placeholder that used to read config.yaml now fails | {Config.*} reads the synced config groups | Switch it to {DeviceConfig.*} |
409 Conflict — "action '…' is already running" | The same release + action is already running | Wait for it, or pause it first |
400 — "action '…' is not defined in the manifest" | The delivered manifest has no such action | The Actions keys of the delivered install.yaml |
| "… targets self action '…' which is not defined in this manifest" | A Deploy/v2 / FleetDeploy/v2 without a ReleaseId names a missing action | Add the action, or fix the Action name |
| "specify either a top-level Tasks (legacy) or Actions (new), not both" / "duplicate action" | Both manifest forms used, or two keys for one action (Install and install) | Keep only Actions, with one key per action |
| Starting a once action does nothing | It already finished Done | The action's status; run a state-changing action (e.g. uninstall) first, or mark it Repeatable: true |
| Task fails with "Launch timeout" / "did not complete within …min" | Installer slow or stuck | LaunchTimeoutSec / ExecutionTimeoutMin; installer behavior |
| Rule task fails on a device that "should" match | Rule condition/field mismatch | The task Rule and the device metadata in the log |
Task[…]: a Verification/Revert must have a target | A Verification/Revert has no preceding Execute sibling and a pure-Group/root parent | Add an Execute before it, or nest it under an actionable task |
Task[…]: Group must have a non-empty tasks list | An empty Group/v2 | Give the group children, or remove it |
Task[…]: a task with CausesReboot: true must have a nested Verification child | A rebooting task with no nested Verification | Add a Verification child under the reboot task |
Task[…]: SSE Verification requires a Message object | An SSE verification with no Message | Add a Message object to match stream frames |
Task[…]: … requires an ExeFile or a Target / requires a Target | A removal/API/SSE method missing its handle/endpoint | Supply ExeFile or Target as the method needs |
Best practices
- Keep tasks idempotent (safe to re-run on resume)
- Prefer metadata-driven timeouts over hard-coded numbers where installers vary
- Scope tasks with rules instead of shipping the wrong artifact to the wrong device
- Keep dependency trees shallow