Skip to main content

JitError

Enum JitError 

Source
pub enum JitError {
    Forbidden {
        target: String,
        runner_group_id: u64,
        message: Option<String>,
    },
    NotFound {
        target: String,
        runner_group_id: u64,
        message: Option<String>,
    },
    Rejected {
        target: String,
        message: Option<String>,
    },
    RateLimited(RateLimited),
    Cancelled,
    Github(GithubError),
}
Expand description

Everything a just-in-time registration can fail with.

The three GitHub answers c4’s specification names are distinct outcomes, not one error, because an operator’s next action differs for each and a caller’s does too: 403 is terminal and needs a permissions or runner-group change, 404 means the target or the group is not there, and 422 means the request itself was rejected.

§None of these carries the encoded configuration

Every variant is built from the request — target, runner group, name — and from GitHub’s own message field. A failing response has no encoded_jit_config to leak, and a 201 that fails to decode is reported through GithubError::Decode, which carries a serde_json::Error and not the body. an_error_never_carries_the_encoded_configuration pins it.

Variants§

§

Forbidden

GitHub refused: the permission or the runner group does not allow it.

Terminal. Nothing retries this, and nothing may: d17 is the record of what a 403 on this family of endpoints means and what it does not.

Fields

§target: String
§runner_group_id: u64
§message: Option<String>
§

NotFound

GitHub found neither the target nor the runner group.

Separate from JitError::Forbidden because v1 measured both answers from the same mistake: a group that does not exist is 404, a group that exists but is not administrable is 403. Collapsing them tells an operator to create a group that is already there.

Fields

§target: String
§runner_group_id: u64
§message: Option<String>
§

Rejected

GitHub rejected the request body: the name or the label set.

Fields

§target: String
§message: Option<String>
§

RateLimited(RateLimited)

GitHub is rate limiting this credential. Resolves by waiting, and is the one failure here that is not about the request.

§

Cancelled

The caller withdrew the registration before it completed.

§

Github(GithubError)

Implementations§

Source§

impl JitError

Source

pub fn is_terminal(&self) -> bool

Whether retrying this exact request could ever produce a different answer.

403, 404 and 422 are all true here, and a 403 must be: c4’s specification says “a 403 must never become a retry loop”, and d17 is the record of a design that spent a spike discovering what a 403 on this family of endpoints means. A rejected credential is terminal too — it resolves by an interactive auth login, not by retrying.

§Why an undecodable answer is terminal, and why it is the expensive one

A body this client cannot parse will not parse on the next attempt, so the answer to the question in the first line is plainly no. What makes it worth spelling out is the cost of getting it wrong here rather than anywhere else: generate-jitconfig answers 201 by completing a registration, and the decode happens after that. A caller that read this as retryable would issue a second registration, and a third, each one a real runner created at GitHub whose one-shot configuration this process then discards — a target silently accumulating registered runners that never come online. So GithubError::Decode and GithubError::Malformed are terminal, and JitError::operator_action answers for both rather than leaving the failure silent.

§Why GithubError::Forbidden and a 404 under Github are not

Both are reachable through the transparent #[from] without passing RestJit::classify, and both would be terminal if they had. They stay as they are because answering them here means keeping a second status-code table beside classify’s, and the two would drift. For the 403 it is worse than untidy: GitHub answers a secondary rate limit with a 403, classify runs RateLimited::detect first for exactly that reason, and a predicate that called the raw variant terminal without repeating that detection would turn the one 403 that resolves by waiting into a permanent failure. An unclassified failure reported as retryable costs a wasted request; an unclassified rate limit reported as terminal costs the registration. The fix for those two is to route them through classify, which every path inside this crate already does.

Source

pub fn rate_limited(&self) -> Option<&RateLimited>

The rate limit behind this failure, when there is one.

Source

pub fn is_cancelled(&self) -> bool

Source

pub fn operator_action(&self) -> Option<String>

What an operator can actually do about this, or None when there is nothing for them to do.

Every terminal outcome has one, which is what “terminal and operator-actionable” means: a failure a human cannot act on and a program will not retry is a dead end. None is correct for a rate limit and a cancellation — both resolve without anyone doing anything.

Trait Implementations§

Source§

impl Debug for JitError

Source§

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

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

impl Display for JitError

Source§

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

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

impl Error for JitError

Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0:

use the Display impl or to_string()

1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0:

replaced by Error::source, which can support downcasting

Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<GithubError> for JitError

Source§

fn from(source: GithubError) -> Self

Converts to this type from the input type.

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> 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> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. 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