subframe7536/maple-font · error · TypeError

{g}({type(g)}) is invalid for __gly

Error message

{g}({type(g)}) is invalid for __gly

What it means

__gly is the internal glyph serializer that converts any glyph spec (str, list of specs, or Clazz) into FEA text. When it receives a value of any other type (int, None, dict, glyph object, etc.) it raises TypeError naming the value and its type. Since __gly is recursive (lists call __gly per element), a single bad element inside a nested list triggers it.

Source

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

__PUNCTUATION_CN_MAP = {
    "“": "quotedblleft",
    "”": "quotedblright",
    "‘": "quoteleft",
    "’": "quoteright",
    "…": "ellipsis",
    "—": "emdash",
}


def __gly(g: str | Clazz | Sequence[str | Clazz] | None) -> str:
    if not g:
        return ""
    if isinstance(g, list):
        return " ".join([__gly(_) for _ in g])
    if isinstance(g, Clazz):
        return g.use()
    if not isinstance(g, str):
        raise TypeError(f"{g}({type(g)}) is invalid for __gly")
    if g in __PUNCTUATION_MAP:
        return __PUNCTUATION_MAP[g]
    if g in __PUNCTUATION_CN_MAP:
        return __PUNCTUATION_CN_MAP[g]
    return g


def __prefix(data: str | Clazz | Sequence[str | Clazz] | None) -> str:
    if data:
        return __gly(data) + " "
    return ""


def __suffix(data: str | Clazz | Sequence[str | Clazz] | None) -> str:
    if data:
        return " " + __gly(data)
    return ""

View on GitHub (pinned to c08fda97fe)

Solutions

  1. Convert the offending value to a str glyph name before passing it (e.g. chr(cp) for codepoints).
  2. Replace None entries with a valid glyph name or filter them out of lists.
  3. Wrap glyph objects with .name (or their string form) if you have custom glyph types.
  4. If you need new supported types, extend __gly in source/py/feature/ast.py rather than passing raw objects.

Example fix

// before
ast.subst(None, 65, None, 'space')  # 65 is an int
// after
ast.subst(None, chr(65), None, 'space')  # 'A'
Defensive patterns

Strategy: type-guard

Validate before calling

def validate_glyph(g):
    if g is None:
        raise ValueError('glyph is None — upstream lookup failed')
    if not isinstance(g, (str, ast.Clazz, list)):
        raise TypeError(f'{g!r} ({type(g).__name__}) must be str, Clazz, or list')
    if isinstance(g, list):
        for x in g: validate_glyph(x)

validate_glyph(my_glyph)
result = ast.gly(my_glyph)

Type guard

def is_glyph_spec(g) -> bool:
    if isinstance(g, ast.Clazz) or isinstance(g, str):
        return True
    return isinstance(g, list) and all(is_glyph_spec(x) for x in g)

Try / catch

try:
    seq = ast.gly(glyph_spec)
except TypeError as e:
    if 'is invalid for __gly' in str(e):
        glyph_spec = str(glyph_spec)  # or chr(codepoint) / glyph.name
        seq = ast.gly(glyph_spec)
    else:
        raise

Prevention

When it happens

Trigger: Passing a non-str/non-Clazz value into ast.gly(), ast.subst()/subst_liga() source or target positions, or nesting such a value inside a list passed to those APIs — e.g. None from a failed lookup, an int codepoint, or a parsed glyph object instead of its name string.

Common situations: Reading glyph names from JSON/YAML where values arrive as numbers or null; building glyph lists programmatically and appending None from an earlier failed operation; passing custom glyph objects instead of str.

Related errors


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