Hmbown/CodeWhale · error
grayscale colors are all resolvable RGB
Error message
grayscale colors are all resolvable RGB
What it means
This is a Rust `Option::expect` panic reading 'grayscale colors are all resolvable RGB': `contrast_ratio(fg, bg)` returned None for a grayscale theme surface, aborting the high-contrast-theme test. The library throws it when contrast_ratio cannot parse one of the theme's colors into RGB — e.g. an unresolved named color, an unparseable hex string, or a non-RGB color representation. Because a grayscale theme is by definition pure RGB values, None here signals the theme builder produced a malformed color.
Solutions
- Log/inspect which color failed resolution; fix the theme definition so every surface_bg and foreground is a concrete, parseable RGB value.
- Make contrast_ratio return Result<Rgb-parse error> so failures name the offending color instead of panicking on Option.
- Validate theme colors at construction time (parse once in the theme builder) so invalid values are rejected where they are created, not in contrast tests.
Example fix
// before
let ratio = contrast_ratio(fg, bg).expect("grayscale colors are all resolvable RGB");
// after
let ratio = contrast_ratio(fg, bg)
.unwrap_or_else(|| panic!("unresolvable RGB in grayscale theme: fg={fg:?} bg={bg:?}")); Defensive patterns
Strategy: validation
Validate before calling
fn all_colors_resolvable(theme: &Theme) -> Result<(), String> {
[theme.surface_bg, theme.panel_bg, theme.composer_bg, theme.elevated_bg]
.iter()
.chain(std::iter::once(&theme.text_default))
.map(|c| c.to_rgb().ok_or_else(|| format!("unresolvable RGB: {c:?}")))
.collect::<Result<Vec<_>, _>>()
.map(|_| ())
} Type guard
fn is_resolvable_rgb(color: &Color) -> bool {
color.to_rgb().is_some()
} Try / catch
let ratio = contrast_ratio(fg, bg).unwrap_or_else(|| {
panic!("contrast_ratio returned None: fg={fg:?} bg={bg:?} is not a resolvable RGB")
}); Prevention
- Parse theme colors to Rgb once at theme construction; reject unresolvable values there.
- Validate grayscale hex strings with a regex (#RRGGBB) before storing them in a theme.
- Never rely on runtime terminal queries to resolve colors used by contrast checks — resolve or fail early.
When it happens
Trigger: Calling `contrast_ratio(fg, bg)` where either argument is a Color that fails RGB resolution: a hex string with the wrong length, a color still stored as a symbolic name, or a theme field populated with None/placeholder before resolution. Any grayscale-theme surface/foreground pair that hits that path panics.
Common situations: Theme definitions referencing named colors that require a terminal-query step to resolve; a refactor changing surface_bg fields from parsed Rgb to raw strings; copy-paste typos in grayscale hex values like '#1111' instead of '#111111'.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- the report must name the failing pair
- truecolor tint
- A pinned task provider requires an explicit model
- Absolute path should not warn
- active catalog write lock
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/0552e8a523ae7467.
Report an issue: GitHub.
Appendix: source
Thrown at crates/palette/src/tests.rs:1142
}
// A painted community theme never gets the exemption.
assert!(!theme_uses_terminal_owned_surfaces(&TOKYO_NIGHT_UI_THEME));
}
#[test]
fn high_contrast_grayscale_theme_clears_body_floor_on_every_surface() {
// The picker tagline claims "Color-minimal high contrast" — hold the
// grayscale theme to the full body-text floor on every surface, not the
// 3:1 secondary-chrome floor.
let theme = GRAYSCALE_UI_THEME;
for fg in [theme.text_body, theme.text_soft, theme.text_muted] {
for bg in [
theme.surface_bg,
theme.panel_bg,
theme.composer_bg,
theme.elevated_bg,
] {
let ratio = contrast_ratio(fg, bg).expect("grayscale colors are all resolvable RGB");
assert!(
ratio >= AA_BODY_CONTRAST,
"grayscale {fg:?} on {bg:?} is {ratio}:1, below the {AA_BODY_CONTRAST}:1 floor",
);
}
}
}
#[test]
fn violation_report_names_pair_and_ratio() {
let mut bad = TOKYO_NIGHT_UI_THEME;
bad.text_muted = bad.surface_bg;
let violations = theme_contrast_violations(&bad);
// The muted-on-surface color fails against all four text surfaces.
let muted_pairs: Vec<_> = violations
.iter()
.filter(|violation| violation.pair.starts_with("text_muted on "))
.collect();View on GitHub (pinned to 433685b202)