Skip to main content
diff compares the manifest against the schedules the revision actually carries and reports, for each entry, whether a deploy would create it, update it — naming the fields that differ — or leave it alone. It writes no schedule, no file, and no session. It is the command to run before a deploy, and the command to run when a deploy has surprised you.

Synopsis

There is no --dry-run here. diff has no side effect to withhold — and the difference between the two commands is exactly that, so deploy --dry-run is not a synonym for this one. See It never creates a session below.

What it reports

Drift first, then one line per manifest entry, then the sessions a deploy would have to create:
Under --json the payload carries blob, create, update (each with alias and fields), unchanged, sessions_to_create and drift, alongside the envelope’s schema_version. The field names in an update line are the manifest’s vocabulary, not the platform’s: target.session, target.revision, target.workflow, target.input_data, target.description, alongside repeat, timezone, enabled, start_date, end_date and whichever timing field applies. They name the lines in your own file you would have to change to make the difference go away.

It never creates a session

This is the one property that makes diff safe to run anywhere, and the one real difference between it and deploy --dry-run. Both commands must resolve every target before they can say anything useful — a comparison against unresolved symbols would be comparing a manifest to nothing. But deploy resolves a session: alias by finding or creating it, and it does that under --dry-run too. diff uses a preview resolver instead: it looks the session up, and when it is simply absent it reports would be created by deploy and moves on. So a manifest naming a session that does not exist yet can be diffed as many times as you like without leaving anything behind. Dry-running the same manifest twice creates the session on the first run. Everything else the two resolvers do is shared code, so they cannot drift apart on what a target resolves to. A session that exists but is closed is refused by both, identically — diff reporting a clean update for an entry deploy was always going to refuse would be worse than failing.

Only the timing field that applies is compared

repeat decides which of cron_expression and invocation_time has meaning, and only that one is compared. The other is not merely irrelevant, it is unclearable. update_schedule sets only the keys it is sent and rejects a null, so a schedule flipped from recurring_cron to one_time keeps its old cron_expression on the record for good. Comparing both regardless would report update (cron_expression) on every diff, and re-issue an update on every deploy, for as long as the entry existed — over a field nobody can remove. A manifest entry carrying both fields is refused at load as MANIFEST_INVALID, which is why this only ever concerns the stored record.

Unlike workflow diff, this one can fail

blobhub workflow diff always exits 0: every difference it can discover, including a package that does not compile, is printed rather than raised. scheduler diff does not make that promise, and the reason is that it validates targets. Reporting that an entry would be created is a claim about what deploy will do. If the entry’s target blob does not resolve, or its workflow: names nothing on the target revision, or its session is closed, then deploy is not going to create anything — so diff raises the same code deploy would, rather than blessing an entry that is already broken:
With a key that cannot read the target blob’s limits — which is most keys, since limits need admin — that message gains a clause saying the definition listing could not be confirmed complete. It still refuses: a typo is far likelier than a revision at its definition cap. What diff still never fails on is a difference. Every create, update and unchanged verdict, and every drifted schedule, is a finding and the exit code stays 0 — so it is not a CI gate for “the manifest is deployed”. Read the --json payload for that.

Drift is reported, never resolved

Schedules the manifest does not cover are listed and left alone. Two things make that worth acting on here rather than filing away:
  • An orphaned schedule is not inert. It keeps firing on its cron, consuming execution quota and appending to its session on every fire.
  • An orphaned schedule with no alias cannot be adopted by any manifest at all, so the drift line calls those out separately, by id. ls explains what to do about one.
pull is what adopts aliased drift; nothing in this group removes drift today.

Errors

Every code above, with its remediation, is in Error codes.

See also