---
description: Template filters must coerce numeric params from YAML/config to prevent int/str TypeError
---

# Filter Type Coercion

Values from YAML, config, and cache can arrive as strings. Python 3 does not allow
mixed-type comparisons (`int <= str` raises `TypeError`). Template filters that
accept numeric params must coerce at entry.

## Template Conventions

- **Avoid arithmetic on YAML/config values** — Do not use `//`, `/`, `%` directly
  on values from config or frontmatter (e.g. `config.content.excerpt_words`).
- **Prefer filters that encapsulate logic** — Use `halve_words=true` on
  `card_excerpt_html` instead of `divided_by(2) | floor` in templates.
- **Use coerce_int or int filter** — When you must pass numeric values to filters,
  apply `| coerce_int(default)` (Bengal) or `| int` (Kida) before arithmetic.

## Affected Filters / Functions

- `truncatewords`, `truncatewords_html`, `card_excerpt_html`, `truncate_chars`
- `get_pagination_items(current_page, total_pages, base_url, window)`
- `Paginator(items, per_page=...)`
- Any filter receiving `config.content.excerpt_words`, `config.content.excerpt_length`, etc.

## Convention: coerce_int for Numeric Params

Use `coerce_int` from `bengal.config.utils` at the start of any filter that accepts
a numeric param used in comparisons or arithmetic:

```python
from bengal.config.utils import coerce_int

def my_filter(text: str, count: int = 30) -> str:
    count = coerce_int(count, 30)
    # ... use count in comparisons
```

## Convention: @template_safe for Error-Prone Functions

When **editing** a template function that may receive invalid types from YAML/config
(e.g. `int` vs `str`), add `@template_safe(default=...)` if the function can raise
`TypeError`, `ValueError`, `AttributeError`, or `KeyError`.

```python
from bengal.rendering.utils.template_safe import template_safe

@template_safe(default="")
def format_date(value: str, fmt: str = "%Y-%m-%d") -> str:
    return datetime.strptime(value, fmt).strftime(fmt)
```

### When to Apply

- **Opportunistic only** — add when you are already editing a template function
- **No bulk migration** — do not proactively convert all ~80 template functions
- Functions that do numeric/attribute/key access on user input from YAML or config
- Functions where a crash would break the entire page render

### When NOT to Apply

- Functions with well-typed internal callers (not exposed to YAML/config values)
- Functions where silent fallback would hide real bugs

### Already Using @template_safe

- 6 functions in `bengal/rendering/template_functions/math_functions.py`

## ConfigSnapshot

`ConfigSnapshot.from_dict` uses `coerce_int` for all numeric content fields
(excerpt_length, excerpt_words, reading_speed, etc.) so invalid YAML does not
raise at load time.

## Audit (2025-03)

Verified coerce_int at entry for numeric params:

| Function | Params | Status |
|----------|--------|--------|
| truncatewords | count | coerce_int(count, 30) |
| truncatewords_html | count | coerce_int(count, 30) |
| truncate_chars | length | coerce_int(length, 120) |
| card_excerpt | words | via truncatewords |
| card_excerpt_html | words, halve_words | coerce_int(words, 30); halve_words=true halves internally |
| get_pagination_items | current_page, total_pages, window | coerce_int each |
| Paginator | per_page | coerce_int(per_page, 10) |
