API-created library workout shows wrong workout structure (cached from old workout)

Summary:
When creating a workout in a library plan via the API (POST /api/v1/athlete/{id}/workouts), the workout structure displayed in the UI is incorrect. It appears to be cached/reused from a previously deleted workout, despite the API returning the correct description and calculated values.

Steps to Reproduce:

  1. Create a new plan folder via API: POST /api/v1/athlete/{id}/folders
  2. Create a workout in that folder via API: POST /api/v1/athlete/{id}/workouts with a simple description:
{
  "folder_id": 649610,
  "name": "Just a test workout - API Test",
  "day": 4,
  "description": "Just a test workout.\n\nWarmup\n- 2m 42%\n\nMain Set\n\n4x\n- 30s 120% 100rpm\n- 4m30s 68%\n\nCooldown\n- 1m 50%",
  "type": "VirtualRide"
}
  1. API returns success with the correct description echoed back:
{
  "id": 71817686,
  "folder_id": 649610,
  "name": "Just a test workout - API Test",
  "description": "Just a test workout.\n\nWarmup\n- 2m 42%\n\nMain Set\n\n4x\n- 30s 120% 100rpm\n- 4m30s 68%\n\nCooldown\n- 1m 50%",
  "moving_time": 6060,
  "icu_training_load": 95,
  ...
}
  1. Open the workout in the Intervals.icu UI

Expected Result:
Workout structure should match the description:

  • Warmup: 2m 42%
  • Main Set: 4x (30s 120% 100rpm, 4m30s 68%)
  • Cooldown: 1m 50%

Actual Result:
Workout structure shows a completely different workout (see screenshot):

  • Warmup: 2m 30%, 2m 33%, 1m30s 36%, 1m30s 39%, 1m 42%, 1m 46%, 1m 50%, 6m 70%, 3m 100%
  • Main Set: 2x (5m 50%, 1m 114%), etc.

This structure is from an old workout I created weeks/months ago and have since deleted.

(see screenshot)

API Response:
Here is the complete response object.

{'athlete_id': 'i166567',
 'attachments': None,
 'carbs_per_hour': None,
 'color': None,
 'day': 4,
 'days': None,
 'description': 'Just a test workout.\n'
                '\n'
                'Warmup\n'
                '- 2m 42%\n'
                '\n'
                'Main Set\n'
                '\n'
                '4x\n'
                '- 30s 120% 100rpm\n'
                '- 4m30s 68%\n'
                '\n'
                'Cooldown\n'
                '- 1m 50%',
 'distance': 0.0,
 'folder_id': 649625,
 'for_week': False,
 'hide_from_athlete': False,
 'icu_intensity': 74.015274,
 'icu_training_load': 21,
 'id': 139,
 'indoor': None,
 'joules': 250980,
 'joules_above_ftp': 6240,
 'moving_time': 1380,
 'name': 'Just a test workout - API Test',
 'plan_applied': None,
 'sub_type': None,
 'tags': None,
 'target': None,
 'targets': ['POWER'],
 'time': None,
 'type': 'VirtualRide',
 'updated': '2025-12-26T13:32:35.904+00:00',
 'workout_doc': {'average_watts': 182,
                 'description': 'Just a test workout.',
                 'distance': 0,
                 'duration': 1380,
                 'locales': [],
                 'normalized_power': 195,
                 'options': {},
                 'polarization_index': 2.85,
                 'steps': [{'duration': 120,
                            'power': {'units': '%ftp', 'value': 42},
                            'warmup': True},
                           {'distance': 0,
                            'duration': 1200,
                            'reps': 4,
                            'steps': [{'cadence': {'units': 'rpm',
                                                   'value': 100},
                                       'duration': 30,
                                       'power': {'units': '%ftp',
                                                 'value': 120}},
                                      {'duration': 270,
                                       'power': {'units': '%ftp',
                                                 'value': 68}}],
                            'text': '4x'},
                           {'cooldown': True,
                            'duration': 60,
                            'power': {'units': '%ftp', 'value': 50}}],
                 'strain_score': {'ss_cp': 24.344982255168986,
                                  'ss_p_max': 0.05623423672097546,
                                  'ss_w_prime': 0.6521008604374656,
                                  'strain_score': 25.053317352327426},
                 'variability_index': 1.0714285714285714,
                 'zoneTimes': [{'color': '#009e80',
                                'id': 'Z1',
                                'max': 55,
                                'maxWatts': 144,
                                'minWatts': 1,
                                'name': 'Z1',
                                'percentRange': '0% - 55%',
                                'secs': 180,
                                'zone': 1},
                               {'color': '#009e00',
                                'id': 'Z2',
                                'max': 75,
                                'maxWatts': 196,
                                'minWatts': 145,
                                'name': 'Z2',
                                'percentRange': '56% - 75%',
                                'secs': 1080,
                                'zone': 2},
                               {'color': '#ffcb0e',
                                'id': 'Z3',
                                'max': 90,
                                'maxWatts': 235,
                                'minWatts': 197,
                                'name': 'Z3',
                                'percentRange': '76% - 90%',
                                'secs': 0,
                                'zone': 3},
                               {'color': '#ff7f0e',
                                'id': 'Z4',
                                'max': 105,
                                'maxWatts': 275,
                                'minWatts': 236,
                                'name': 'Z4',
                                'percentRange': '91% - 105%',
                                'secs': 0,
                                'zone': 4},
                               {'color': '#dd0447',
                                'id': 'Z5',
                                'max': 120,
                                'maxWatts': 314,
                                'minWatts': 276,
                                'name': 'Z5',
                                'percentRange': '106% - 120%',
                                'secs': 120,
                                'zone': 5},
                               {'color': '#6633cc',
                                'id': 'Z6',
                                'max': 150,
                                'maxWatts': 393,
                                'minWatts': 315,
                                'name': 'Z6',
                                'percentRange': '121% - 150%',
                                'secs': 0,
                                'zone': 6},
                               {'color': '#504861',
                                'id': 'Z7',
                                'max': 999,
                                'maxWatts': 9999,
                                'minWatts': 394,
                                'name': 'Z7',
                                'percentRange': '151% +',
                                'secs': 0,
                                'zone': 7},
                               {'color': '#ffa00e',
                                'gap': True,
                                'id': 'SS',
                                'max': 94,
                                'maxWatts': 246,
                                'min': 88,
                                'minWatts': 230,
                                'name': 'Sweet Spot',
                                'percentRange': '88% - 94%',
                                'secs': 0,
                                'zone': 8}]}}

Key Observation:
The API response contains the correct description that was sent, yet the UI displays a completely different workout structure. This suggests the workout steps are being retrieved from a cache or lookup that references old/deleted workout data, rather than being parsed from the description field.

Additional Details:

  • The plan name is correct: “Just a test plan - API Test”
  • The workout name is correct: “Just a test workout - API Test”
  • The duration (1h40m) and load (95) match the API response
  • The API response description field contains the correct text
  • But the rendered workout steps/structure are from an old deleted workout

Environment:

  • API endpoint: POST /api/v1/athlete/{id}/workouts
  • Workout created inside a plan folder (not as a calendar event)

Screenshot attached: Shows the workout editor with correct name but wrong workout structure.

ok, now it seems to be working correctly… I did not changed anything. In fact I tried it multiple times before posting here and the workout created contained in different runs different workout structures of past workouts…