subframe7536/maple-font · error · Exception

Invalid axe value, range: [{min_value}, {max_value}]

Error message

Invalid axe value, range: [{min_value}, {max_value}]

What it means

instantiate() pins a variable font to a static instance. For every axis defined in the font's fvar table it reads the requested value from the axes config (falling back to the axis default) and validates it lies within [minValue, maxValue]. If any configured axis value is out of the font's declared range, this generic Exception is raised.

Source

Thrown at source/py/task/merge_font/utils.py:151

def instantiate(input_font_path: str, output_font_path: str, config: dict) -> None:
    """
    Instantiate a variable font with the given configuration.

    :param input_font_path: The path to the input font file.
    :param output_font_path: The path to the output font file.
    :param config: A dictionary containing axis configurations.
    """
    f = Font(input_font_path)
    coordinates = {}
    instance = NamedInstance()
    for a in f.t_fvar.table.axes:
        axis_tag = a.axisTag
        min_value = a.minValue
        max_value = a.maxValue
        val = config.get(axis_tag, a.defaultValue)
        if min_value > val or max_value < val:
            raise Exception(f"Invalid axe value, range: [{min_value}, {max_value}]")
        coordinates[axis_tag] = val

    instance.coordinates = coordinates

    static_font, file_base_name = var2static(f, instance)
    static_font.save(output_font_path)
    static_font.close()
    f.close()


def _get_glyph_bounds(font, glyph_name):
    """Get (yMin, yMax) of a glyph by name from 'glyf' table."""
    glyph_order = font.getGlyphOrder()
    if glyph_name not in glyph_order:
        raise ValueError(f"Glyph '{glyph_name}' not found in font.")
    glyf = font["glyf"]
    glyph = glyf[glyph_name]
    if glyph.numberOfContours == 0:

View on GitHub (pinned to c08fda97fe)

Solutions

  1. Open the font and check its fvar axes ranges (fonttools ttx -t fvar, or a tool like fontdrop) and set the axis value inside [min, max].
  2. Clamp the value in code: max(min_value, min(max_value, val)) before calling instantiate.
  3. Verify the axis tag keys in the config exactly match the font's axis tags (case-sensitive, e.g. 'wght', 'wdth', 'slnt').

Example fix

# before
axes: {wght: 1000}   # font range is 100-900

# after
axes: {wght: 900}
Defensive patterns

Strategy: validation

Validate before calling

from fontTools.ttLib import TTFont
f = TTFont(font_path)
ranges = {a.axisTag: (a.minValue, a.maxValue) for a in f["fvar"].axes}
for tag, val in axes.items():
    lo, hi = ranges[tag]
    assert lo <= val <= hi, f"{tag}={val} out of range [{lo}, {hi}]"

Type guard

def axis_in_range(val, lo: float, hi: float) -> bool:
    return isinstance(val, (int, float)) and lo <= val <= hi

Try / catch

try:
    instantiate(font_path, out_path, axes)
except Exception as e:
    if "Invalid axe value" in str(e):
        axes = {t: min(max(v, *sorted(bounds))) for ...}  # clamp and retry
    else:
        raise

Prevention

When it happens

Trigger: Passing an 'axes' dict to a font source config where a value for an axis tag (e.g. wght: 1200 when the font supports 100-900, or wght: 50) is below a.minValue or above a.maxValue, or a non-numeric value that breaks the comparison.

Common situations: Copying axis values from one font family to another with different ranges; typos like 'Wght' vs 'wght' matched to the wrong axis; requesting weights (e.g. 950, 1050) a variable font does not expose; passing strings from YAML that compare badly to numbers.

Related errors


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