Skip to main content

FailureReason

Enum FailureReason 

Source
pub enum FailureReason {
    JitRequestFailed,
    JitExpired,
    RunnerPackageUnverified,
    RunnerVersionRejected,
    ProcessStartFailed,
    ProcessExitedUnexpectedly,
    RegistrationTimedOut,
    TerminatedAfterRegistrationTimeout,
    Other(String),
}
Expand description

Why an attempt failed.

Other exists so e3 is not blocked by a reason this task did not anticipate; crates/domain/src/attempt.rs belongs to b1 and e3 cannot extend this enum itself.

Nothing that reaches this type may contain a credential. It is written to the journal by b2 and rendered by g2, and 07-security.md’s log scan runs over both. A JIT blob, an Authorization header, or a token in an Other string would defeat that gate from inside the domain, where the redacting log sink (d1) never gets a chance to see it.

Variants§

§

JitRequestFailed

generate-jitconfig did not return a configuration.

§

JitExpired

The configuration was never claimed and expired (flow 4.4).

§

RunnerPackageUnverified

The runner package’s published checksum was absent or did not match (05-infrastructure.md: the agent fails closed).

§

RunnerVersionRejected

GitHub rejects runners more than 30 days behind the latest release (01-current-architecture.md, edge case 7). Terminal and operator-actionable, never retried.

§

ProcessStartFailed

The child process could not be spawned.

§

ProcessExitedUnexpectedly

The child process exited before it could do its one job.

Only for a process that is actually gone. A runner still running past its startup deadline is Self::RegistrationTimedOut, not this: g2 renders these strings to an operator, and telling one that a process “exited unexpectedly” while it is visible in Task Manager spends the credibility of every other message this product prints.

§

RegistrationTimedOut

The runner process is up but never registered with GitHub inside its startup window.

Split from Self::ProcessExitedUnexpectedly because it is accurate and because it points an operator somewhere else entirely. A process that exited is a crash to investigate — logs, exit code, a corrupt runner package. A process that is alive and unregistered has almost always failed to reach GitHub: a proxy, a firewall, a DNS answer, an expired or wrong-scoped configuration. Those are configuration and networking fixes, and an operator sent to the wrong one of the two loses the time this distinction exists to save.

§

TerminatedAfterRegistrationTimeout

The agent stopped a runner process that had not registered inside its startup window.

The dead-process counterpart of Self::RegistrationTimedOut, and it exists because that one cannot be reused here. By the time this reason is recorded the process is gone — the agent signalled it — so rendering “the runner process is running but did not register” would tell an operator a process is up that they can see is not. That is the same false liveness claim Self::ProcessExitedUnexpectedly’s documentation says spends the credibility of every other message this product prints, and tests::no_decision_calls_a_dead_process_live is what holds the line.

It points where Self::RegistrationTimedOut points, not where Self::ProcessExitedUnexpectedly points. The runner never reached GitHub — a proxy, a firewall, a DNS answer, an expired or wrong-scoped configuration — and the exit is the agent’s own doing rather than evidence of a crash. An operator sent to logs and exit codes for this is investigating the wrong machine.

Who records it. e3, and only e3. recovery_decision cannot derive it: a process this agent killed and a process that crashed on its own present the same RecoveryObservation, so the distinguishing fact has to be journalled as terminate-intent before the signal is sent and read back afterwards. See RecoveryDecision::Terminate for the window that obligation closes.

§

Other(String)

Anything else. Must carry no credential.

Implementations§

Source§

impl FailureReason

Source

pub const ALL: [FailureReason; 9]

One value of every variant, the counterpart of AttemptState::ALL.

Other’s detail is empty because what a caller enumerates is the variant; no consumer should read the string out of this constant.

This list is hand-written, and what keeps it honest is not its own length. A length written as 9 next to nine elements asserts nothing — that was the defect in the assertion this constant replaced. What catches a new variant is the exhaustive, wildcard-free match in tests::earliest_state_producing, which stops the test target compiling the moment one is added and so puts the author in front of this list.

The residual gap, measured rather than assumed. An author who adds a variant, writes its Display arm and its earliest_state_producing arm, and then adds it to neither this constant nor the test’s cases table, gets a green suite with the variant untested. Adding it to exactly one of the two fails the length check; adding it to neither does not.

Re-measured when TerminatedAfterRegistrationTimeout was added, because a gap described once and never re-run is a gap nobody knows still exists. With the variant declared and both match arms written but neither list touched, cargo test -p runner-manager-domain was green — its lib target reported 128 passed; 0 failed — with the ninth variant unreachable and untested. Adding it to this constant alone then failed the length check with left: 8 / right: 9.

And the compiler never points at this constant. A const array is unaffected by a new variant, so nothing here errors. What stops the author is Display::fmt’s match (E0004) and then tests::earliest_state_producing’s, and neither of those mentions this list — which is why a note pointing back here sits at each of those two match sites, where the author is actually standing.

This is closable in stable Rust, and is hand-written anyway. A local macro_rules! that declares the enum and emits ALL from the same variant list needs no dependency and no unstable feature (std::mem::variant_count is unstable, but a declarative macro is not the same thing). It is not used here because every variant of this enum carries several paragraphs of its own documentation explaining what an operator should do about it, and variants declared inside a macro invocation are markedly worse to read and to rustdoc. That is a legibility trade, deliberately taken — not an impossibility. If the documentation ever thins out, the macro is the better answer.

Trait Implementations§

Source§

impl Clone for FailureReason

Source§

fn clone(&self) -> FailureReason

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for FailureReason

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for FailureReason

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl Display for FailureReason

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Eq for FailureReason

Source§

impl PartialEq for FailureReason

Source§

fn eq(&self, other: &FailureReason) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl Serialize for FailureReason

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl StructuralPartialEq for FailureReason

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more