subframe7536/maple-font · error · TypeError

id should > 0 and < 100 in Character Variants, current is {i

Error message

id should > 0 and < 100 in Character Variants, current is {id}

What it means

Character Variants (cvXX) OpenType features are identified by an id 1–99, which the library formats as the tag cv01–cv99. FeatureWithDocs subclasses for Character Variants validate id in __init__ and raise TypeError when the id is out of range, because ids outside 1–99 cannot produce a valid two-digit cv tag. This is a constructor-time constraint, so it fires as soon as the feature object is created.

Source

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

        self.desc = desc
        self.example = example
        Feature.__init__(self, tag, content, version)

    def desc_item(self):
        return f"- [v{self.version}] {self.tag}: {self.desc}"


class CharacterVariant(FeatureWithDocs):
    def __init__(
        self,
        id: int,
        desc: str,
        content: Clazz | Lookup | Line | list,
        version: str,
        example: str,
    ):
        if id < 1 or id > 99:
            raise TypeError(
                f"id should > 0 and < 100 in Character Variants, current is {id}"
            )
        FeatureWithDocs.__init__(
            self,
            id=id,
            tag=f"cv{id:02d}",
            desc=desc,
            content=content,
            version=version,
            example=example,
        )

    def get_name_lines(self) -> list[Line]:
        _name = re.sub(
            REGEXP, "", self.desc.replace("`", "").replace(EMPTY_FEAT_SYMBOL, " ")
        ).strip()
        return [
            Line("cvParameters {"),

View on GitHub (pinned to c08fda97fe)

Solutions

  1. Set the feature id to an integer in the range 1–99.
  2. If you need more than 99 variants, split them across additional feature types or reuse/merge existing cv ids.
  3. Clamp/validate ids from external config before constructing: `if not 1 <= id <= 99: skip or remap`.

Example fix

// before
CharacterVariants(id=100, desc='...', content=..., version='1.0', example='...')
// after
id = 100
assert 1 <= id <= 99, f'cv id must be 1-99, got {id}'
CharacterVariants(id=99, desc='...', content=..., version='1.0', example='...')
Defensive patterns

Strategy: validation

Validate before calling

def check_cv_id(id):
    if not isinstance(id, int) or not (1 <= id <= 99):
        raise ValueError(f'cv id must be int 1-99, got {id}')

check_cv_id(id)
feature = CharacterVariants(id=id, ...)

Type guard

def is_valid_cv_id(id) -> bool:
    return isinstance(id, int) and 1 <= id <= 99

Try / catch

try:
    feature = CharacterVariants(id=id, desc=desc, content=content, version=version, example=example)
except TypeError as e:
    if 'Character Variants' in str(e):
        logging.error(f'remap cv id {id} into 1-99')
        id = min(max(int(id), 1), 99)
        feature = CharacterVariants(id=id, desc=desc, content=content, version=version, example=example)
    else:
        raise

Prevention

When it happens

Trigger: Constructing a Character Variants feature (any class whose __init__ checks `if id < 1 or id > 99`) with id=0, a negative id, or id >= 100.

Common situations: Using a 0-based index straight from a loop without adding 1; defining cv100 for a 'hundredth' variant not realizing the OpenType cv tag space stops at cv99; loading a config file where ids were serialized off-by-one.

Related errors


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