fix(cli): do not panic on proposals with an unrecognised action - #2127
Merged
Conversation
`dre proposals filter` and `dre proposals list` abort outright when the
governance canister returns a proposal whose action this build of `dre`
does not know about:
thread 'main' panicked at rs/cli/src/commands/proposals/mod.rs:94:44:
called `Option::unwrap()` on a `None` value
Candid decodes a variant it cannot resolve in an `opt` field into `None`,
so every action added to governance after a given `dre` release turns
`proposal.action` into `None` for that release. NNS proposal 143574
("Set the very first standard engine replica version to e3d101b2.",
action `UpdateStandardEngineReplicaVersion`) did exactly that when it was
submitted on 2026-08-14, and because it is the newest proposal in the
`ic-os-version-deployment` topic, *every* `dre proposals filter` call has
panicked since — at any `--limit`, and with no topic filter at all.
That includes the calls the mainnet rollout DAG uses to decide whether a
subnet update proposal already exists, so the NNS subnet (tdb26) step of
rollout `manual__2026-08-11T09:28:45+00:00` has been failing on every
retry since 07:00 UTC on 2026-08-17.
Both call sites already degrade gracefully when the conversion returns
`Err` — `filter` logs the error and skips the proposal, `list` falls back
to serialising the raw `ProposalInfo` — so the panic was the only thing
turning one unrecognised action into a total outage.
Handle a `None` action by falling back to the canister's
`self_describing_action`, which exists precisely to be understood without
the schema of a specific proposal type, or to an empty payload when the
canister did not send one. The proposal then still appears in listings
with its id, title, topic and status intact instead of disappearing. The
remaining `unwrap()`s in the same conversion become errors for the same
reason: a malformed proposal should cost that one proposal, not the whole
command.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
pietrodimarco-dfinity
enabled auto-merge (squash)
August 17, 2026 10:00
r-birkner
approved these changes
Aug 17, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What is broken
Every
dre proposals filteranddre proposals listinvocation currently panics against mainnet:It reproduces at
--limit 5,--limit 100, and with no topic filter at all — the offending proposal is the newest one in the topic, so nothing narrows past it.Root cause
Line 94 was
payload: match proposal.action.unwrap(). Candid decodes a variant it cannot resolve in anoptfield intoNonerather than failing, so any action added to the governance canister after a givendrerelease makesproposal.actionNonefor that release — andunwrap()then kills the process.The trigger is NNS proposal 143574, "Set the very first standard engine replica version to e3d101b2.", submitted 2026-08-14T16:37:42Z and still open. Its action is the native
UpdateStandardEngineReplicaVersionvariant (noteaction_nns_function: null— it is not anExecuteNnsFunctionlike everything else in that topic), andic-nns-governance-apias pinned inCargo.lock(rev = abf88f22) has no such variant.Impact
dfinity/dre-airflow'srollout_ic_os_to_mainnet_subnetsDAG shells out todre proposals filterto decide whether a subnet update proposal already exists. The NNS subnet (tdb26) step of rolloutmanual__2026-08-11T09:28:45+00:00has therefore failed on every retry since 07:00 UTC on 2026-08-17 —batch_30.create_proposal_if_none_existsis stuck inup_for_retry, and the two downstream sensors (WaitForProposalAcceptance,has_proposal_executed) go through the same call, so they would fail too.The fix
Both call sites already handle a failed conversion gracefully —
filterlogs the error and skips the proposal,listfalls back to serialising the rawProposalInfo. The panic was the only thing turning one unrecognised action into a total outage.Noneaction now falls back to the canister'sself_describing_action(which exists precisely to be understood without the schema of a specific proposal type), or to an empty payload when the canister did not send one. The proposal keeps its id, title, topic and status in listings instead of vanishing from them.unwrap()s in the same conversion return errors instead, on the same principle: a malformed proposal should cost that one proposal, not the whole command.Most of the diff is the re-indentation from wrapping the existing arms in
Some(action) => match action { … }—git diff -wshows the ~20 real lines.Verification
Three regression tests in
rs/cli/src/commands/proposals/mod.rscover an unrecognised action with and without aself_describing_action, and the malformed-ProposalInfopaths.Built from this branch and run against mainnet:
The empty payload is compatible with the Airflow consumer, which filters on
r["payload"].get("subnet_id").Follow-up (not in this PR)
Bumping the pinned
ic-nns-governance-apiwould letdredecodeUpdateStandardEngineReplicaVersionproperly rather than just tolerating it. This PR is deliberately limited to stopping the crash, since a mainnet rollout is blocked on it.🤖 Generated with Claude Code