wezterm/wezterm · error · anyhow::Error
failed to create feature from {}
Error message
failed to create feature from {} What it means
feature_from_string wraps HarfBuzz's hb_feature_from_string, which parses OpenType feature selectors like 'kern', '-liga', 'ss01=2' or 'calt=0' into an hb_feature_t. The parser is strict about syntax and returns false (mapped to this Err) for anything that is not tag, optional +/- sign, optional =value or =start:end ranges. wezterm feeds it entries from the user's harfbuzz_features config array, so a malformed entry surfaces here.
Source
Thrown at wezterm-font/src/hbwrap.rs:39
}
pub const IS_PNG: hb_tag_t = hb_tag(b'p', b'n', b'g', b' ');
#[allow(unused)]
pub const IS_SVG: hb_tag_t = hb_tag(b's', b'v', b'g', b' ');
pub const IS_BGRA: hb_tag_t = hb_tag(b'B', b'G', b'R', b'A');
pub fn language_from_string(s: &str) -> Result<hb_language_t, Error> {
unsafe {
let lang = hb_language_from_string(s.as_ptr() as *const c_char, s.len() as i32);
ensure!(!lang.is_null(), "failed to convert {} to language", s);
Ok(lang)
}
}
pub fn feature_from_string(s: &str) -> Result<hb_feature_t, Error> {
unsafe {
let mut feature = mem::zeroed();
ensure!(
hb_feature_from_string(
s.as_ptr() as *const c_char,
s.len() as i32,
&mut feature as *mut _,
) != 0,
"failed to create feature from {}",
s
);
Ok(feature)
}
}
#[derive(Debug)]
pub struct Blob {
blob: *mut hb_blob_t,
}
impl Drop for Blob {View on GitHub (pinned to 3ff7522b96)
Solutions
- Rewrite each entry in HarfBuzz syntax: optional leading +/-, a 4-character tag, then optional =number or =start:end — e.g. 'calt=1' not 'calt on'
- Remove empty strings and stray whitespace from the harfbuzz_features list in your wezterm config
- Test a suspect feature string with `hb-shape --features='YOURSTRING' /path/to/font.ttf` — HarfBuzz's CLI reports the same parse error
- If you need boolean wording in config, translate it before calling: 'kern off' -> 'kern=0', 'liga on' -> 'liga=1'
Example fix
-- before (wezterm config)
harfbuzz_features = { "calt off", "liga on" } -- spaces -> hb_feature_from_string fails
-- after
harfbuzz_features = { "calt=0", "liga=1" } Defensive patterns
Strategy: validation
Validate before calling
fn plausible_feature(s: &str) -> bool {
let s = s.strip_prefix(['+', '-']).unwrap_or(s);
let mut parts = s.splitn(2, '=');
let tag = parts.next().unwrap_or("");
let tag_ok = tag.len() == 4 && tag.bytes().all(|b| b.is_ascii_alphanumeric() || b == b'_');
match parts.next() {
None => tag_ok,
Some(v) => tag_ok && !v.is_empty() && v.bytes().all(|b| b.is_ascii_digit() || b == b':'),
}
} Type guard
fn is_valid_hb_feature(s: &str) -> bool { plausible_feature(s) } Try / catch
for spec in features {
match feature_from_string(spec) {
Ok(f) => buf.add_feature(f),
Err(err) => log::warn!("ignoring bad harfbuzz feature {spec:?}: {err:#}"),
}
} Prevention
- Write features in canonical HarfBuzz syntax: 'ss01', '-liga', 'calt=0', 'kern=2:5' — never space-separated boolean wording
- Validate each harfbuzz_features entry at config load and reject the config early with the offending string
- Test suspect strings with hb-shape --features='...' before committing them
When it happens
Trigger: Passing strings with wrong separator or value syntax: 'calt on' (space instead of '='), 'liga:' (dangling range), 'kern=maybe' (non-numeric value), '' (empty), or '=2' (missing tag). Correct forms: 'smcp', '-liga', 'ss02=1', 'kern=0'.
Common situations: wezterm config harfbuzz_features written in CSS-ish syntax ('liga off') instead of HarfBuzz syntax ('-liga'); copy-pasted snippets from font-documentation sites that use human wording; trailing whitespace/quotes introduced by scripting the config.
Related errors
- failed to convert {} to language
- hb_buffer_create failed
- failed to wrap font as blob
- failed to create harfbuzz Face
- failed to create harfbuzz Font
AI-assisted analysis of wezterm/wezterm@3ff7522b96 (2026-08-20).
Data as JSON: /api/errors/e1f12a19c64c3d3c.
Report an issue: GitHub.