> For the complete documentation index, see [llms.txt](https://docs.tryterra.co/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.tryterra.co/planned-workouts-api/coercion-warnings.md).

# Coercion Warnings

### What Are Coercion Warnings?

When a workout feature can't be fully represented on a provider's platform, Terra makes adjustments and returns **warnings**. The workout is still created, but some details may differ from what you requested.

Warnings also travel the other way. When `GET /plannedWorkouts` returns a workout the athlete created on the provider's side, Terra converts that workout into the same workout body it uses everywhere else, and anything it could not carry across arrives as a warning on that item.

### Response Format

```json
{
  "status": "success",
  "planned_workout_id": "2048",
  "provider_workout_id": "88112233/44556677",
  "warnings": [
    {
      "path": "workout.sport",
      "message": "sport 'rowing' not directly supported by Garmin, using GENERIC"
    },
    {
      "path": "workout.step_blocks[1].steps[0].intensity_targets[1].target_type",
      "message": "Garmin uses heart rate percentage of max HR, not threshold HR. The percentage values will be interpreted as % of max HR."
    }
  ]
}
```

Read `warnings`. It is always present and is an empty array when nothing was adjusted. `coercion_warnings` carries the same list, is deprecated, and on `GET /plannedWorkouts` is a JSON string rather than an array.

### Warnings on Listed Workouts

`GET /plannedWorkouts` returns workouts created through Terra and workouts the athlete created on the provider's side (`is_external: true`). Both kinds carry the same `workout` body and the same `warnings` array.

On a Terra-created workout, `warnings` says what was lost pushing your template to the provider. On a provider-created workout, it says what was lost reading the provider's workout back into a Terra workout body. For example, Intervals.icu nests a repeat group only when the workout text uses its documented syntax, a blank line before and after the `3x` line. Written any other way, the steps come back flat.

Two warning paths tell you where a missing body came from:

| Warning path                           | What it means                                                                                                                      |
| -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `workout` (with `workout: null`)       | Terra could not load the stored body of a workout you created.                                                                     |
| `step_blocks` (with `step_blocks: []`) | The provider returned the workout, but its step file could not be fetched. Name, sport and estimates are there, the steps are not. |

Provider-created workouts are listed for Garmin, Wahoo, Suunto, TrainingPeaks and Intervals.icu. Intervals.icu is read only: you can list its workouts but not push to it, so its warnings always come from reading.

### Common Warning Scenarios

#### Missing Athlete Parameters

**Cause:** Workout uses percentage targets but required parameter wasn't provided.

```json
{
  "path": "workout.step_blocks[0].steps[0].intensity_targets[0]",
  "message": "HR threshold percentage target requires a threshold HR baseline. Using default threshold HR of 170 bpm. Provide threshold_heart_rate for accurate targets."
}
```

**Fix:** Include the required parameter in your schedule request:

```json
POST /workouts/123/plan?user_id=X
{
  "planned_date": "2026-02-10",
  "threshold_heart_rate": 165
}
```

#### Unsupported Sport Type

**Cause:** Provider doesn't support the workout's sport.

```json
{
  "path": "workout.sport",
  "message": "sport 'rowing' not directly supported by Garmin, using GENERIC"
}
```

**Impact:** Workout created but may display incorrectly on device.

#### Too Many Targets

**Cause:** Step has more targets than provider supports.

```json
{
  "path": "workout.step_blocks[0].steps[0].intensity_targets",
  "message": "Zepp supports one primary target and one optional cadence secondary target, ignoring 2 extra target(s)"
}
```

**Fix:** Prioritize your most important target as the first one.

#### Unsupported Target Type

**Cause:** Provider doesn't support the target type.

```json
{
  "path": "workout.step_blocks[0].steps[0].intensity_targets[0].target_type",
  "message": "Target type 'cadence' not supported by Garmin for this sport. Using OPEN instead."
}
```

#### Swimming Pool Length Missing

**Cause:** Swimming workout without pool length.

```json
{
  "path": "workout.pool_length_meters",
  "message": "Pool length not provided for swimming workout. Defaulting to 25 meters."
}
```

**Fix:** Set `pool_length_meters` in template or schedule request.

#### Unrecognized Exercise Name

**Cause:** Strength exercise name doesn't match Garmin's catalog.

```json
{
  "path": "workout.step_blocks[0].steps[0].strength.exercise_name",
  "message": "Could not determine category for exercise name 'CUSTOM_EXERCISE', dropping exercise name/category but keeping weight data"
}
```

**Impact:** Step still created, weight preserved, but exercise name/category not shown.

**Fix:** Use a recognized exercise name.

#### Block Completion Coercion

**Cause:** Provider doesn't support the block completion type.

```json
{
  "path": "workout.step_blocks[1].completion_condition",
  "message": "Time-based block completion not supported. Block will execute once."
}
```

#### Intensity Type Coercion

**Cause:** Provider doesn't support the intensity type.

```json
{
  "path": "workout.step_blocks[0].steps[0].intensity_type",
  "message": "RECOVERY intensity coerced to ACTIVE."
}
```

#### Estimates Ignored (TrainingPeaks)

**Cause:** The workout supplies `estimated_tss`, `estimated_intensity_factor` or `estimated_duration_seconds`, and at least one step target is not RPE.

```json
{
  "path": "workout.estimated_tss",
  "message": "TrainingPeaks derives this from the step targets when any target is not RPE; the supplied value is ignored"
}
```

**Impact:** Terra forwards `estimated_tss`, `estimated_intensity_factor`, `estimated_distance_meters` and `estimated_calories` to TrainingPeaks when you plan a workout and when you reschedule it. TrainingPeaks recalculates TSS, intensity factor and duration from the athlete's own thresholds whenever a step uses anything other than an RPE target. You get one warning per value you supplied. Distance and calories are unaffected.

**Fix:** Use RPE targets throughout if your own TSS and intensity factor need to stick. Otherwise treat TrainingPeaks' numbers as the source of truth.

`estimated_tss` must be between 0 and 9999 and `estimated_intensity_factor` between 0 and 5. Outside those ranges the template is rejected with a 400 when you create it, not coerced.

#### Sync Window (Zepp)

**Cause:** Workout scheduled outside the provider's sync window.

```json
{
  "path": "planned_date",
  "message": "workout date is outside Zepp's 7-day sync window; it will not appear on the device until it falls within range"
}
```

**Impact:** The planned workout is saved and returned by the API, but it won't appear on the device until it enters the 7-day window (today to today + 6 days). It is pushed the next time you create, reschedule or delete a workout for that user, which refreshes the window. Nothing is pushed in the background.

#### Unsupported Sport (Zepp)

**Cause:** Sport type not natively supported by Zepp.

```json
{
  "path": "workout.sport",
  "message": "sport 'strength' not supported by Zepp, defaulting to 'RUNNING'. Supported sports: RUNNING, CYCLING, LAP_SWIMMING"
}
```

**Impact:** Workout is created but displays as a running workout on the device.

#### Cadence as Primary Target (Zepp)

**Cause:** Cadence provided as the only target without a primary target.

```json
{
  "path": "workout.step_blocks[0].steps[0].intensity_targets[0].target_type",
  "message": "cadence is only supported as a secondary target on Zepp; provide a primary target (pace, power, heart_rate) alongside it"
}
```

**Fix:** Add a primary target (heart rate, power, pace, or speed) and use cadence as a secondary target.

#### Swim Stroke Ignored (Zepp)

**Cause:** Zepp does not support stroke type metadata.

```json
{
  "path": "workout.step_blocks[0].steps[0].swimming.stroke_type",
  "message": "Zepp does not support stroke types, 'butterfly' will be ignored"
}
```

**Impact:** Swimming workout is created but stroke type is not displayed on the device.

### Handling Warnings in Your App

#### Log for Debugging

```python
response = create_planned_workout(workout_id, user_id, date, params)

for warning in response.get('warnings', []):
    logger.warning("Workout coercion: %s - %s", warning['path'], warning['message'])
```

#### Show to Users (Optional)

For user-facing apps, you might surface relevant warnings:

```javascript
if (response.warnings?.length > 0) {
  showNotification({
    type: 'info',
    message: 'Some workout features were adjusted for your device.',
    details: response.warnings.map(w => w.message)
  });
}
```

#### Ignore If Acceptable

Many warnings are informational. If the workout still works for your use case, you can safely ignore them.

### Preventing Warnings

1. **Check Provider Compatibility** before building workouts
2. **Always provide athlete parameters** for percentage-based targets
3. **Use single targets** for maximum compatibility
4. **Use recognized exercise names** for strength training
5. **Set pool length** for swimming workouts
6. **Stick to common intensity types**: warmup, active, rest, cooldown
