subframe7536/maple-font · error · Exception

Unkown feature: {feature.tag}

Error message

Unkown feature: {feature.tag}

What it means

clone_empty() creates placeholder (empty) copies of documented features, but it only knows how to clone Character Variants and Stylistic Sets feature types; anything else (e.g. a base Feature, FeatureWithDocs subtype it doesn't recognize, or cv00/ss00 tags it can't parse) falls through to a generic Exception. The message is misspelled 'Unkown' in the source. It is raised by cv_list_italic, cv_list_regular, and ss_list_regular when building the empty-variant feature lists.

Source

Thrown at source/py/feature/ast.py:584

def clone_empty(feature: FeatureWithDocs, desc_prefix: str = ""):
    if isinstance(feature, CharacterVariant):
        return CharacterVariant(
            id=feature.id,
            desc=desc_prefix + EMPTY_FEAT_SYMBOL + feature.desc,
            content=EMPTY_FEAT_CONTENT,
            version=feature.version,
            example=feature.example,
        )
    if isinstance(feature, StylisticSet):
        return StylisticSet(
            id=feature.id,
            desc=desc_prefix + EMPTY_FEAT_SYMBOL + feature.desc,
            content=EMPTY_FEAT_CONTENT,
            version=feature.version,
            example=feature.example,
        )
    raise Exception(f"Unkown feature: {feature.tag}")


def filter_empty(features: list[FeatureWithDocs], full: bool):
    if full:
        return features

    return list(filter(lambda x: EMPTY_FEAT_SYMBOL not in x.desc, features))

View on GitHub (pinned to c08fda97fe)

Solutions

  1. Pass only Character Variants / Stylistic Sets feature instances to the cv_list_* / ss_list_* builders.
  2. Check feature.tag is a valid cvXX/ssXX tag and the object is of the expected class before adding it to the list.
  3. If you added a new documented-feature type, extend clone_empty() in source/py/feature/ast.py to handle it.

Example fix

// before
cv_list_regular(my_features + [my_custom_feature])
// after
cloneable = [f for f in my_features if f.tag.startswith(('cv', 'ss'))]
cv_list_regular(cloneable)
Defensive patterns

Strategy: validation

Validate before calling

def only_cloneable(features):
    return [f for f in features if f.tag.startswith(('cv', 'ss')) and isinstance(f, FeatureWithDocs)]

cv_list_regular(only_cloneable(features), full=full)

Type guard

def is_cloneable_feature(f) -> bool:
    return isinstance(f, FeatureWithDocs) and (f.tag.startswith('cv') or f.tag.startswith('ss'))

Try / catch

try:
    empties = cv_list_regular(features)
except Exception as e:
    if str(e).startswith('Unkown feature:'):
        tag = str(e).rsplit(': ', 1)[1]
        logging.warning(f'skipping non-clonable feature {tag}')
        empties = cv_list_regular([f for f in features if f.tag != tag])
    else:
        raise

Prevention

When it happens

Trigger: Calling cv_list_italic/cv_list_regular/ss_list_regular with a features list containing feature objects whose tag isn't a recognizable cvXX/ssXX documented-feature type — e.g. passing plain Feature or FeatureWithDocs instances, or a custom subclass.

Common situations: Mixing custom feature classes into the list passed to the list builders; refactoring feature classes so a type is no longer an instance of the expected CharacterVariants/StylisticSets classes; passing full features instead of documented ones.

Related errors


AI-assisted analysis of subframe7536/maple-font@c08fda97fe (2026-08-28). Data as JSON: /api/errors/ed027ce6e4c3bc09. Report an issue: GitHub.