# Tera usage and troubleshooting

Use this guide to build, validate, and troubleshoot Tera templates in Notification Center. The guide covers common issues, error-prevention techniques, and best practices when writing dynamic templates for connectors, presets, and routing rules.

For full syntax, filters, and operators, see the [Tera syntax reference](https://keats.github.io/tera/docs/).

## 1. Handle missing fields to avoid rendering failures

If a required field is missing from the data, template rendering fails.

To prevent this, always provide a default value or check if the variable is defined before using it.

### Use a default value

```text
{{ featureName | default('Unknown Feature') }}
```

### Check if a variable exists

```text
{%- if featureName is defined -%}
  {{ featureName }} is released!
{%- elif bugDescription is defined -%}
  Bug reported: {{ bugDescription }}
{%- else -%}
  NA
{%- endif -%}
```

Undefined variables cause render errors. Always guard or default optional fields.

## 2. Prevent JSON parsing failures in presets

### Symptom

- Template fails to render.
- JSON parsing errors appear.
- Happens when inserting unescaped text into JSON fields.

### Cause

If the rendered value contains unescaped quotes, the JSON field becomes invalid.

### Solution: sanitize fields with `json_encode`

Always encode free-form strings inserted into JSON.

**Correct:**

```text
{
  "description": {{ alertDef.description | json_encode }}
}
```

**Incorrect:**

```text
{
  "description": "Some description with "Quotes inside" "
}
```

`json_encode` escapes internal quotes and ensures valid JSON.

## 3. Fix `is` conditions used on missing fields

### Symptom

Conditions like:

```text
alertDef.entityLabels["key"] is containing("value")
```

fail with: `Test call failed`.

### Cause

String tests fail if the field is undefined.

### Solution: guard with `is defined`

```text
alertDef.entityLabels["key"] is defined
and alertDef.entityLabels["key"] is containing("value")
```

Regex example:

```text
alertDef.entityLabels["service"] is defined
and alertDef.entityLabels["service"] is matching("checkout")
```

## 4. Format dates from timestamps

Convert timestamps to readable dates using the `date` filter.

Tera expects timestamps in seconds, so convert milliseconds or other units before formatting. Use `epoch_seconds` to safely handle raw numeric timestamps:

```text
{{ maintenanceDate | epoch_seconds | date(format="%d/%m/%y - %H:%M:%S") }}
```

If timezone information is also needed (using `%Z` or `%z`), supply a `timezone` parameter to the `date` function. In nearly all cases, timestamps are UTC, so specify UTC explicitly:

```text
{{ maintenanceDate | epoch_seconds | date(format="%d/%m/%y - %H:%M:%S %Z", timezone="UTC") }}
```

Without the timezone parameter, the template will fail to render.

However, if the input value is already a date string (e.g., `2022-02-02`, `2022-02-02T12:34:56`, `2022-02-02T12:34:56+0100`), the timezone parameter is not required—Tera will infer UTC or the correct timezone automatically.

## 5. Define variables to simplify logic

Assign variables using `set` to avoid repetition.

```text
{%- set priority = alert.highestPriority | default(value=alert.priority) -%}
{%- if priority == "P1" -%}
  critical
{%- elif priority == "P2" -%}
  error
{%- elif priority == "P3" -%}
  warning
{%- else -%}
  info
{%- endif -%}
```

### Variable scope inside loops

Use `set_global` when a value must persist outside a loop.

```text
{% set default_key = "default_key" %}

{% for s in keys %}
  {% if s[0] == alertDef.someService %}
    {% set_global default_key = "" %}
    {% break %}
  {% endif %}
{% endfor %}

{{ default_key }}
```

## 6. Iterate over key–value pairs

```text
{% for key, value in _context.entityLabels %}
  {{ key }}: {{ value }}{% if not loop.last %}, {% endif %}
{% endfor %}
```

Use this pattern to print all labels dynamically.

## 7. Remove extra spaces and newlines

Tera preserves whitespace. Trim using `{%-` and `-%}`.

```text
{%- for item in items -%}
  Example
{%- endfor %}
```

Keeps rendered output compact.

## 8. Explore available variables

Print the entire context:

```text
{{ get_context() | json_encode(pretty=true) }}
```

This reveals objects such as `alertDef`, `alert`, `case`, and `_context`.

Note

Use schema references for accurate field names:

- [Alerts schema](https://coralogix.com/docs/user-guides/notification-center/entity-types/introduction/#work-with-alert-entities)
- [Cases schema](https://coralogix.com/docs/user-guides/notification-center/entity-types/introduction/#work-with-case-entities)

## 9. Extract alert log example

The `get_log` function extracts alert log examples. Use it only in alert-related templates.

```text
{{ get_log() | json_encode(pretty=true) }}
```

## 10. Project JSON object fields

To extract a subset of JSON object fields, use the `pick` function. Construct the resulting object by using either `include` or `exclude` projection parameters:

```text
{# The resulting object will contain `priority`, `metrics.value` keys #}
{{ get_log() | pick(include = ["priority", "metrics.value"]) | json_encode(pretty = true) }}

{# The resulting object will contain everything except `metrics` key #}
{{ get_log() | pick(exclude = ['metrics']) | json_encode(pretty = true) }} 

{# Error. It doesn't support brackets based indexing, use .<index> instead #}
{{ get_log() | pick(include = ['groups[0].value']) | json_encode(pretty = true) }}
{# The resulting object will contain {groups: [null, {value: ...}]} #}
{{ get_log() | pick(include = ['groups.1.value']) | json_encode(pretty = true) }}
```

## Next steps

See a complete walkthrough of routing alert notifications to Slack in [Example: route alerts to Slack](https://coralogix.com/docs/user-guides/notification-center/user-scenarios/alerts-to-slack/index.md).
