subframe7536/maple-font · error · TypeError
Invalid item to flatten: {item}
Error message
Invalid item to flatten: {item} What it means
flatten_to_lines() normalizes feature content into a flat list of Line objects; it accepts items that expose .state(), Line instances, and Lookup/Feature objects (expanded via their .state()). Any other object type in the content list is unsupported and raises TypeError 'Invalid item to flatten'. It guards the internal content model of features, lookups, and lines.
Source
Thrown at source/py/feature/ast.py:559
yield data
def flatten_to_lines(
data: Line | Clazz | Lookup | Feature | list | tuple,
) -> list[Line]:
result = []
for item in recursive_iterate(data):
if not item:
continue
elif isinstance(item, Clazz):
result.append(item.state())
elif isinstance(item, Line):
result.append(item)
elif isinstance(item, (Lookup, Feature)):
result.extend(item.state())
else:
raise TypeError(f"Invalid item to flatten: {item}")
return result
EMPTY_FEAT_CONTENT = [Line("# Placeholder"), subst(None, "EMquad", None, "space")]
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(View on GitHub (pinned to c08fda97fe)
Solutions
- Wrap plain text in Line(): content = [Line('# my comment'), ...].
- Wrap substitution rules in ast.Lookup(...) or use ast.subst()/ast.subst_liga() helpers that return supported objects.
- Ensure custom content classes implement .state() returning Line(s).
- Flatten nested lists yourself before assigning content (list comprehension over sublists).
Example fix
// before
Feature(id=1, desc='...', content=['# comment', subst(...)], ...)
// after
Feature(id=1, desc='...', content=[Line('# comment'), subst(...)], ...) Defensive patterns
Strategy: validation
Validate before calling
def validate_content(content):
for item in content:
if not (isinstance(item, (ast.Line, ast.Lookup, ast.Feature)) or hasattr(item, 'state')):
raise TypeError(f'{item!r} must be Line, Lookup, Feature, or expose .state()')
validate_content(my_content)
feature = FeatureWithDocs(id=1, desc='d', content=my_content, version='1.0', example='e') Type guard
def is_valid_content_item(item) -> bool:
return isinstance(item, (ast.Line, ast.Lookup, ast.Feature)) or callable(getattr(item, 'state', None)) Try / catch
try:
lines = feature.state()
except TypeError as e:
if 'Invalid item to flatten' in str(e):
bad = str(e).split(': ', 1)[1]
logging.error(f'replace {bad!r} with Line()/Lookup() wrapper')
else:
raise Prevention
- Always wrap raw strings in ast.Line(...) and rules in ast.Lookup(...).
- Use ast.subst/subst_liga helpers so content items are always the right type.
- Give custom content classes a .state() method returning Line objects.
When it happens
Trigger: Passing a feature content list containing raw strings, dicts, integers, or arbitrary objects to anything that flows through flatten_to_lines — Feature/FeatureWithDocs construction content, Lookup, or calls to create/state/cls_states.
Common situations: Hand-writing content as plain strings like '# comment' instead of Line('# comment'); forgetting to wrap substitutions in a Lookup; passing a tuple instead of list; mixing in output of another library.
Related errors
- Invalid class_list, must ends with [@Var, @HexLetter]
- id should > 0 and < 100 in Character Variants, current is {i
- id should > 0 and < 20 in Stylistic Sets, current is {id}
- {g}({type(g)}) is invalid for __gly
- Unkown feature: {feature.tag}
AI-assisted analysis of subframe7536/maple-font@c08fda97fe (2026-08-28).
Data as JSON: /api/errors/51477afad8ef470f.
Report an issue: GitHub.