AprilNEA/OpenLogi · error
could not derive synthetic Unifying serial
Error message
could not derive synthetic Unifying serial
What it means
Thrown by `identity_plan` while building the sanitized identity plan for a receiver-routed fixture: `generate_synthetic_identity(SyntheticIdentityKind::UnifyingReceiverSerial, ordinal).as_bytes()` returned `None`, so no synthetic serial bytes could be derived for the receiver serial number. The generator is expected to produce bytes for every kind/ordinal, so this signals that the synthetic-identity policy refused or cannot encode this input — an internal invariant rather than user error.
Solutions
- Check the ordinal passed into `identity_plan` is within the range `openlogi-fixture`'s synthetic identity policy supports, and use a valid one.
- Verify `openlogi-cli` and `openlogi-fixture` versions are from the same workspace/build — regenerate with a consistent build.
- Inspect `generate_synthetic_identity` for `UnifyingReceiverSerial`: if `as_bytes()` is None by design for some inputs, fix the caller's input or extend the policy.
- If reproducible with a stock ordinal, file/fix it as a bug — this path should be unreachable for valid inputs.
Example fix
// before: as_bytes() can be None, error surfaces here
let serial = generate_synthetic_identity(SyntheticIdentityKind::UnifyingReceiverSerial, ordinal)
.as_bytes()
.ok_or_else(|| anyhow!("could not derive synthetic Unifying serial"))?
.to_vec();
// after: assert the invariant with context for diagnosis
let serial = generate_synthetic_identity(SyntheticIdentityKind::UnifyingReceiverSerial, ordinal)
.as_bytes()
.with_context(|| format!("synthetic Unifying serial undefined for ordinal {ordinal}"))?
.to_vec(); Defensive patterns
Strategy: try-catch
Validate before calling
// verify the ordinal is inside the policy's supported range before deriving
let ordinal = profile_ordinal(profile)?;
assert!(ordinal < openlogi_fixture::MAX_SYNTHETIC_ORDINAL,
"ordinal {ordinal} exceeds synthetic identity policy range"); Try / catch
match identity_plan(profile, route, ordinal) {
Err(e) if msg_contains(&e, "could not derive synthetic Unifying serial") => {
eprintln!("identity policy cannot encode ordinal {ordinal}; \
check openlogi-fixture version and ordinal range");
std::process::exit(1);
}
other => other?,
} Prevention
- Keep `openlogi-cli` and `openlogi-fixture` built from the same workspace — no version skew.
- Don't hand-edit fixture profile ordinals; let the contribute flow assign them.
- After upgrading, re-derive identity plans rather than reusing cached ones.
- Treat this error as a bug report trigger — valid ordinals should never fail.
When it happens
Trigger: Running the fixture contribute flow for a `DeviceRoute::Receiver`-style profile where the identity generator returns no byte representation for the given `SyntheticIdentityKind::UnifyingReceiverSerial` and ordinal — e.g. an ordinal outside the range the policy encodes, or a policy/derive mismatch after a fixture-crate change.
Common situations: Contributing fixtures with a hand-edited or out-of-range device ordinal; running a newer CLI against an older `openlogi-fixture` policy (or vice versa) where the serial kind lost its byte encoding; a regression in the synthetic-identity policy.
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
- resumable fixture cases contain a non-UTF-8 entry
- resumable fixture cases contain unexpected file
- raw-HID devices support profile-only contributions
- the selected device has no stable synthetic identity for…
- the selected direct-capture target does not match the…
AI-assisted analysis of AprilNEA/OpenLogi@e846e6f4b4 (2026-09-13).
Data as JSON: /api/errors/039b623901efc1f7.
Report an issue: GitHub.
Appendix: source
Thrown at crates/openlogi-cli/src/cmd/fixture/contribute.rs:341
) -> Result<HidCassetteIdentityPlan> {
let mut plan = HidCassetteIdentityPlan::default();
let model = match selected_route {
DeviceRoute::Bolt { receiver_uid, slot } => {
plan.insert(
SanitizedIdentityKind::ReceiverUniqueId,
receiver_uid.as_bytes().to_vec(),
)?;
selected_model(profile, selected_route, *slot)?
}
DeviceRoute::Unifying { receiver_uid, slot } => {
let ordinal = classify_synthetic_profile_identity(
SyntheticIdentityKind::UnifyingReceiverRoute,
receiver_uid,
)?;
let serial =
generate_synthetic_identity(SyntheticIdentityKind::UnifyingReceiverSerial, ordinal)
.as_bytes()
.ok_or_else(|| anyhow!("could not derive synthetic Unifying serial"))?
.to_vec();
plan.insert(SanitizedIdentityKind::ReceiverSerialNumber, serial)?;
selected_model(profile, selected_route, *slot)?
}
DeviceRoute::Direct { .. } => selected_model(
profile,
selected_route,
openlogi_core::hid::DIRECT_DEVICE_INDEX,
)?,
DeviceRoute::RawHid { .. } => {
bail!("raw-HID devices support profile-only contributions")
}
};
add_model_identities(&mut plan, model)?;
Ok(plan)
}
fn selected_model<'a>(View on GitHub (pinned to e846e6f4b4)