{"record":{"id":"e1f12a19c64c3d3c","repo":"wezterm/wezterm","slug":"failed-to-create-feature-from","errorCode":null,"errorMessage":"failed to create feature from {}","messagePattern":"failed to create feature from (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"wezterm-font/src/hbwrap.rs","lineNumber":39,"sourceCode":"}\n\npub const IS_PNG: hb_tag_t = hb_tag(b'p', b'n', b'g', b' ');\n#[allow(unused)]\npub const IS_SVG: hb_tag_t = hb_tag(b's', b'v', b'g', b' ');\npub const IS_BGRA: hb_tag_t = hb_tag(b'B', b'G', b'R', b'A');\n\npub fn language_from_string(s: &str) -> Result<hb_language_t, Error> {\n    unsafe {\n        let lang = hb_language_from_string(s.as_ptr() as *const c_char, s.len() as i32);\n        ensure!(!lang.is_null(), \"failed to convert {} to language\", s);\n        Ok(lang)\n    }\n}\n\npub fn feature_from_string(s: &str) -> Result<hb_feature_t, Error> {\n    unsafe {\n        let mut feature = mem::zeroed();\n        ensure!(\n            hb_feature_from_string(\n                s.as_ptr() as *const c_char,\n                s.len() as i32,\n                &mut feature as *mut _,\n            ) != 0,\n            \"failed to create feature from {}\",\n            s\n        );\n        Ok(feature)\n    }\n}\n\n#[derive(Debug)]\npub struct Blob {\n    blob: *mut hb_blob_t,\n}\n\nimpl Drop for Blob {","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/wezterm/wezterm/blob/3ff7522b9617ec920f7fbdb22b57e93d42d93ee9/wezterm-font/src/hbwrap.rs#L21-L57","documentation":"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.","triggerScenarios":"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'.","commonSituations":"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.","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'"],"exampleFix":"-- before (wezterm config)\nharfbuzz_features = { \"calt off\", \"liga on\" } -- spaces -> hb_feature_from_string fails\n\n-- after\nharfbuzz_features = { \"calt=0\", \"liga=1\" }","handlingStrategy":"validation","validationCode":"fn plausible_feature(s: &str) -> bool {\n    let s = s.strip_prefix(['+', '-']).unwrap_or(s);\n    let mut parts = s.splitn(2, '=');\n    let tag = parts.next().unwrap_or(\"\");\n    let tag_ok = tag.len() == 4 && tag.bytes().all(|b| b.is_ascii_alphanumeric() || b == b'_');\n    match parts.next() {\n        None => tag_ok,\n        Some(v) => tag_ok && !v.is_empty() && v.bytes().all(|b| b.is_ascii_digit() || b == b':'),\n    }\n}","typeGuard":"fn is_valid_hb_feature(s: &str) -> bool { plausible_feature(s) }","tryCatchPattern":"for spec in features {\n    match feature_from_string(spec) {\n        Ok(f) => buf.add_feature(f),\n        Err(err) => log::warn!(\"ignoring bad harfbuzz feature {spec:?}: {err:#}\"),\n    }\n}","preventionTips":["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"],"tags":["harfbuzz","rust","ffi","text-shaping","opentype"],"backgroundTag":"harfbuzz-invalid-feature","analyzedSha":"3ff7522b9617ec920f7fbdb22b57e93d42d93ee9","analyzedAt":"2026-08-20T04:47:31.434Z","contentChangedAt":"2026-08-20T04:47:31.434Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}