bevyengine/bevy · critical
Color compression flag must be `COMPRESS_COLOR_FLOAT16` or `
Error message
Color compression flag must be `COMPRESS_COLOR_FLOAT16` or `COMPRESS_COLOR_UNORM8`
What it means
In the compressed-format selection match (crates/bevy_mesh/src/mesh.rs:1069-1086), the color arm is entered when the compression flags intersect COMPRESS_COLOR_RESERVED_BIT. The code then requires COMPRESS_COLOR_FLOAT16 or COMPRESS_COLOR_UNORM8 to be set and treats anything else as unreachable!. With the current bit layout (UNORM8 = 1 << shift, FLOAT16 = 2 << shift, RESERVED = both bits) any value intersecting the reserved bit necessarily contains one of the two, so this panic guards against bit-layout drift or flags built through from_bits_retain/serialization from an incompatible version.
Source
Thrown at crates/bevy_mesh/src/mesh.rs:1085
Some(VertexFormat::Snorm16x2)
}
ATTRIBUTE_COLOR_ID
if self
.attribute_compression
.intersects(MeshAttributeCompressionFlags::COMPRESS_COLOR_RESERVED_BIT) =>
{
if self
.attribute_compression
.contains(MeshAttributeCompressionFlags::COMPRESS_COLOR_FLOAT16)
{
Some(VertexFormat::Float16x4)
} else if self
.attribute_compression
.contains(MeshAttributeCompressionFlags::COMPRESS_COLOR_UNORM8)
{
Some(VertexFormat::Unorm8x4)
} else {
unreachable!("Color compression flag must be `COMPRESS_COLOR_FLOAT16` or `COMPRESS_COLOR_UNORM8`")
}
}
ATTRIBUTE_JOINT_WEIGHT_ID
if self
.attribute_compression
.contains(MeshAttributeCompressionFlags::COMPRESS_JOINT_WEIGHT) =>
{
Some(VertexFormat::Unorm16x4)
}
_ => None,
}
}
/// Create compressed attribute values for the given attribute ID and attribute values. Return `None` if the compression flag is not set or the attribute values can't be compressed.
///
/// Panics when compressing positions but `aabb` is `None`, or when compressing UVs but corresponding `uv_ranges` is `None`.
fn create_compressed_attribute_values(
&self,View on GitHub (pinned to 396ca72708)
Solutions
- Build flags only from the named constants: use COMPRESS_COLOR_FLOAT16 or COMPRESS_COLOR_UNORM8 (or the with_color helper) instead of raw bit manipulation
- Never round-trip compression flag bits between different Bevy versions; re-derive them from settings
- If this fires with purely documented flag combinations, report it as a Bevy bug: the internal invariant is broken
Example fix
// before
let flags = MeshAttributeCompressionFlags::from_bits_retain(raw_bits); // may carry impossible color bits
let mesh = mesh.compressed_mesh(flags, false);
// after
let flags = MeshAttributeCompressionFlags::COMPRESS_POSITION
| MeshAttributeCompressionFlags::COMPRESS_COLOR_UNORM8; // named constants only
let mesh = mesh.compressed_mesh(flags, false); Defensive patterns
Strategy: validation
Validate before calling
fn color_compression_flags_valid(flags: MeshAttributeCompressionFlags) -> bool {
let color_used = flags.intersects(
MeshAttributeCompressionFlags::COMPRESS_COLOR_RESERVED_BIT,
);
!color_used
|| flags.contains(MeshAttributeCompressionFlags::COMPRESS_COLOR_FLOAT16)
|| flags.contains(MeshAttributeCompressionFlags::COMPRESS_COLOR_UNORM8)
}
assert!(color_compression_flags_valid(flags)); Prevention
- Build MeshAttributeCompressionFlags only from named constants or with_color
- Never persist raw flag bits across Bevy upgrades; re-derive them from settings
- Avoid from_bits_retain for compression flags; validate unknown bits and reject them
When it happens
Trigger: Constructing MeshAttributeCompressionFlags with color bits in an impossible state: only possible via from_bits_retain/from_bits_transmute on unknown raw bits, or flags (de)serialized by a different Bevy version with a different color bit layout, followed by a call that resolves compressed vertex formats (e.g. compressed_mesh).
Common situations: Persisting MeshAttributeCompressionFlags across Bevy upgrades; hand-assembling bitflags from raw integers in tooling; unsafe transmute of flag values.
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
- cannot convert VertexAttributeValues::{variant} to {into}
- Mesh winding inversion does not work for primitive topology
- Indices weren't in chunks according to topology
- Mesh access error: {0}
- Source mesh does not have primitive topology TriangleList or
AI-assisted analysis of bevyengine/bevy@396ca72708 (2026-08-20).
Data as JSON: /api/errors/5799f2c240835d92.
Report an issue: GitHub.