For the complete documentation index, see llms.txt. For a full content snapshot, see llms-full.txt. Append .md to any kestra.io/docs/* URL for plain Markdown.

Kestra 2.0 replaces the conditions and preconditions system across all trigger types.

  • All trigger types (Schedule, Webhook, HTTP, Flow, and others) — the conditions list is removed in favor of a top-level when Pebble expression.
  • Flow triggers — both conditions and preconditions are removed in favor of dependsOn (upstream flow entries) and window (time window configuration).
  • Flow trigger outputs — scoped by flow ID: trigger.outputs.<flowId>.<key>.
  • Input rendering failures — now create a FAILED execution instead of silently dropping the event.

Both conditions and preconditions are removed in Kestra 2.0. Flows that still use them will fail to parse after upgrading.

conditionswhen on all triggers

All trigger types gain a top-level when property containing a Pebble expression. When the expression evaluates to true, the trigger fires; when false, it is skipped. This replaces the conditions list, which required a fully qualified Java type for every filtering need and did not compose cleanly across trigger types.

when expression context

The variables available in a when expression depend on the trigger type:

Trigger typeAvailable variables
Scheduletrigger.date, trigger.timestamp
Webhooktrigger.body, trigger.headers
Flownamespace, flowId, state, labels, outputs, hasRetryAttempt

New Pebble helper functions

These functions are introduced specifically for when expressions to replace verbose date formatting patterns:

FunctionSignatureDescription
isPublicHolidayisPublicHoliday(date, countryCode[, subDivision])Returns true if the date is a public holiday. Backed by Jollyday. Optional third argument for sub-divisions (e.g. 'IDF').
isDayWeekInMonthisDayWeekInMonth(date, dayOfWeek, position)Returns true if the date is the Nth occurrence of a weekday in its month. position accepts FIRST, SECOND, THIRD, FOURTH, or LAST.
isWeekendisWeekend(date)Returns true if the date falls on Saturday or Sunday.
isLastWorkingDayisLastWorkingDay(date[, workingDays])Returns true if the date is the last working day of its month. Working days default to Monday–Friday. Optional second argument overrides which days count as working days.
dayOfWeekdayOfWeek(date)Returns the day name as a string (MONDAY, TUESDAY, …, SUNDAY).
hourOfDayhourOfDay(date)Returns the hour as an integer (0–23).
dayOfMonthdayOfMonth(date)Returns the day of the month as an integer (1–31).
monthOfYearmonthOfYear(date)Returns the month as an integer (1–12).

Existing Pebble filters (startsWith, endsWith, date) and operators (and, or, not, ==, !=, >, <, >=, <=) cover the remaining use cases.

Schedule: specific day of week

Before

triggers:
- id: schedule
type: io.kestra.plugin.core.trigger.Schedule
cron: "0 9 * * *"
conditions:
- type: io.kestra.plugin.core.condition.DayWeek
dayOfWeek: MONDAY

After

triggers:
- id: schedule
type: io.kestra.plugin.core.trigger.Schedule
cron: "0 9 * * *"
when: "{{ dayOfWeek(trigger.date) == 'MONDAY' }}"

Schedule: weekends only

Before

triggers:
- id: schedule
type: io.kestra.plugin.core.trigger.Schedule
cron: "0 11 * * *"
conditions:
- type: io.kestra.plugin.core.condition.Weekend

After

triggers:
- id: schedule
type: io.kestra.plugin.core.trigger.Schedule
cron: "0 11 * * *"
when: "{{ isWeekend(trigger.date) }}"

Schedule: weekdays only (exclude weekends)

Before

triggers:
- id: schedule
type: io.kestra.plugin.core.trigger.Schedule
cron: "0 9 * * *"
conditions:
- type: io.kestra.plugin.core.condition.Not
conditions:
- type: io.kestra.plugin.core.condition.Weekend

After

triggers:
- id: schedule
type: io.kestra.plugin.core.trigger.Schedule
cron: "0 9 * * *"
when: "{{ not isWeekend(trigger.date) }}"

Schedule: exclude Sundays

Before

triggers:
- id: schedule
type: io.kestra.plugin.core.trigger.Schedule
cron: "0 9 * * *"
conditions:
- type: io.kestra.plugin.core.condition.Not
conditions:
- type: io.kestra.plugin.core.condition.DayWeek
dayOfWeek: SUNDAY

After

triggers:
- id: schedule
type: io.kestra.plugin.core.trigger.Schedule
cron: "0 9 * * *"
when: "{{ dayOfWeek(trigger.date) != 'SUNDAY' }}"

Schedule: public holidays

Before

triggers:
- id: schedule
type: io.kestra.plugin.core.trigger.Schedule
cron: "0 11 * * *"
conditions:
- type: io.kestra.plugin.core.condition.PublicHoliday
country: FR

After

triggers:
- id: schedule
type: io.kestra.plugin.core.trigger.Schedule
cron: "0 11 * * *"
when: "{{ isPublicHoliday(trigger.date, 'FR') }}"

With a sub-division: {{ isPublicHoliday(trigger.date, 'FR', 'IDF') }}.

Schedule: workdays only (not weekend, not public holiday)

Before

triggers:
- id: schedule
type: io.kestra.plugin.core.trigger.Schedule
cron: "0 11 * * *"
conditions:
- type: io.kestra.plugin.core.condition.Not
conditions:
- type: io.kestra.plugin.core.condition.PublicHoliday
country: FR
- type: io.kestra.plugin.core.condition.Weekend

After

triggers:
- id: schedule
type: io.kestra.plugin.core.trigger.Schedule
cron: "0 11 * * *"
when: "{{ not isWeekend(trigger.date) and not isPublicHoliday(trigger.date, 'FR') }}"

Schedule: first Monday of the month

Before

triggers:
- id: schedule
type: io.kestra.plugin.core.trigger.Schedule
cron: "0 11 * * 1"
conditions:
- type: io.kestra.plugin.core.condition.DayWeekInMonth
dayOfWeek: MONDAY
dayInMonth: FIRST

After

triggers:
- id: schedule
type: io.kestra.plugin.core.trigger.Schedule
cron: "0 11 * * 1"
when: "{{ isDayWeekInMonth(trigger.date, 'MONDAY', 'FIRST') }}"

Schedule: date range

Before

triggers:
- id: schedule
type: io.kestra.plugin.core.trigger.Schedule
cron: "*/5 * * * *"
conditions:
- type: io.kestra.plugin.core.condition.DateTimeBetween
after: "2025-12-31T23:59:59Z"
before: "2026-06-30T23:59:59Z"

After

triggers:
- id: schedule
type: io.kestra.plugin.core.trigger.Schedule
cron: "*/5 * * * *"
when: "{{ trigger.date > '2025-12-31T23:59:59Z' and trigger.date < '2026-06-30T23:59:59Z' }}"

Schedule: specific hours only

Before

triggers:
- id: schedule
type: io.kestra.plugin.core.trigger.Schedule
cron: "0 * * * *"
conditions:
- type: io.kestra.plugin.core.condition.TimeBetween
after: "08:00:00"
before: "17:00:00"

After

triggers:
- id: schedule
type: io.kestra.plugin.core.trigger.Schedule
cron: "0 * * * *"
when: "{{ hourOfDay(trigger.date) >= 8 and hourOfDay(trigger.date) < 17 }}"

Schedule: combining multiple conditions

Before (first Monday of the month, skip public holidays in France)

triggers:
- id: schedule
type: io.kestra.plugin.core.trigger.Schedule
cron: "0 11 * * *"
conditions:
- type: io.kestra.plugin.core.condition.DayWeekInMonth
dayOfWeek: MONDAY
dayInMonth: FIRST
- type: io.kestra.plugin.core.condition.Not
conditions:
- type: io.kestra.plugin.core.condition.PublicHoliday
country: FR

After

triggers:
- id: schedule
type: io.kestra.plugin.core.trigger.Schedule
cron: "0 11 * * *"
when: "{{ isDayWeekInMonth(trigger.date, 'MONDAY', 'FIRST') and not isPublicHoliday(trigger.date, 'FR') }}"

Webhook: filter by body

Before

triggers:
- id: webhook
type: io.kestra.plugin.core.trigger.Webhook
key: 4wjtkzwVGBM9yKnjm3yv8r
conditions:
- type: io.kestra.plugin.core.condition.Expression
expression: "{{ trigger.body.hello == 'world' }}"

After

triggers:
- id: webhook
type: io.kestra.plugin.core.trigger.Webhook
key: 4wjtkzwVGBM9yKnjm3yv8r
when: "{{ trigger.body.hello == 'world' }}"

Webhook: filter by header and body

Before

triggers:
- id: webhook
type: io.kestra.plugin.core.trigger.Webhook
key: myKey
conditions:
- type: io.kestra.plugin.core.condition.Expression
expression: "{{ trigger.headers['X-Event-Type'] == 'deploy' }}"
- type: io.kestra.plugin.core.condition.Expression
expression: "{{ trigger.body.environment == 'production' }}"

After

triggers:
- id: webhook
type: io.kestra.plugin.core.trigger.Webhook
key: myKey
when: "{{ trigger.headers['X-Event-Type'] == 'deploy' and trigger.body.environment == 'production' }}"

Multiple Expression conditions combine into a single when expression using and / or.

What replaces what

Old condition typeNew when expression
DayWeek (e.g. MONDAY){{ dayOfWeek(trigger.date) == 'MONDAY' }}
Weekend{{ isWeekend(trigger.date) }}
Not > Weekend (weekdays only){{ not isWeekend(trigger.date) }}
Not > DayWeek SUNDAY (exclude Sundays){{ dayOfWeek(trigger.date) != 'SUNDAY' }}
PublicHoliday (country: FR){{ isPublicHoliday(trigger.date, 'FR') }}
Not > PublicHoliday + Weekend (workdays){{ not isWeekend(trigger.date) and not isPublicHoliday(trigger.date, 'FR') }}
DayWeekInMonth (MONDAY, FIRST){{ isDayWeekInMonth(trigger.date, 'MONDAY', 'FIRST') }}
DateTimeBetween (after/before){{ trigger.date > '2025-12-31T23:59:59Z' and trigger.date < '2026-06-30T23:59:59Z' }}
TimeBetween (08:00-17
)
{{ hourOfDay(trigger.date) >= 8 and hourOfDay(trigger.date) < 17 }}
Expression (custom Pebble)Direct when expression, no wrapper needed
Expression on webhook body/headers{{ trigger.body.field == 'value' }} or {{ trigger.headers['X-Key'] == 'value' }}
Multiple Expression conditionsCombined with and / or in a single when

For the full list of Pebble calendar helper functions (isWeekend, isPublicHoliday, isDayWeekInMonth, isLastWorkingDay, hourOfDay, etc.), see the date and calendar helpers reference.

conditions and preconditionsdependsOn on Flow triggers

Both conditions (execution-level types such as ExecutionStatus, ExecutionFlow, ExecutionNamespace) and preconditions (upstream flow lists with time windows) are replaced by a single dependsOn list. Each entry declares one upstream dependency with typed properties.

dependsOn entry properties

PropertyTypeDefaultDescription
flowIdstringExact flow ID to match. Omit to match any flow.
namespacestringExact namespace to match. Use when for prefix or pattern matching.
stateslistall terminal states and PAUSEDExecution states that satisfy this entry.
labelsmapLabels the upstream execution must carry (all must match).
whenstringPebble expression for additional filtering on the upstream execution context.

Both flowId and namespace use exact matching: namespace: company.team matches only company.team, not company.team.project. For prefix or pattern matching, use when with startsWith or endsWith.

When no states are specified on a dependsOn entry, the trigger evaluates against all terminal states (SUCCESS, WARNING, FAILED, KILLED, CANCELLED, RETRIED, SKIPPED, RESUBMITTED) and PAUSED. Specify states explicitly to narrow the match.

Single upstream flow

The preconditions block and the conditions-based approach both map to a single dependsOn entry.

Before (from preconditions)

triggers:
- id: after_extract
type: io.kestra.plugin.core.trigger.Flow
preconditions:
id: flows
flows:
- namespace: company.team
flowId: extract
states: [SUCCESS]

Before (from conditions)

triggers:
- id: on_completion
type: io.kestra.plugin.core.trigger.Flow
states: [SUCCESS]
conditions:
- type: io.kestra.plugin.core.condition.ExecutionFlow
namespace: company.team
flowId: extract

After

triggers:
- id: after_extract
type: io.kestra.plugin.core.trigger.Flow
dependsOn:
- flowId: extract
namespace: company.team
states: [SUCCESS]

Multiple upstream flows with a deadline

Before

triggers:
- id: after_staging
type: io.kestra.plugin.core.trigger.Flow
preconditions:
id: staging_deps
timeWindow:
type: DAILY_TIME_DEADLINE
deadline: "09:00:00+01:00"
flows:
- namespace: company.team
flowId: stg_sales
states: [SUCCESS]
- namespace: company.team
flowId: stg_marketing
states: [SUCCESS]

After

triggers:
- id: after_staging
type: io.kestra.plugin.core.trigger.Flow
dependsOn:
- flowId: stg_sales
namespace: company.team
- flowId: stg_marketing
namespace: company.team
window:
deadline: "09:00:00"

states defaults to all terminal states and PAUSED when omitted. window moves to the trigger level. See Window configuration for all window types and the onMiss property.

Multiple upstream flows (from multipleConditions)

Before

triggers:
- id: multiple_listen_flow
type: io.kestra.plugin.core.trigger.Flow
multipleConditions:
- id: multiple
window: P1D
windowAdvance: P0D
conditions:
flow_a:
type: io.kestra.plugin.core.condition.ExecutionFlow
namespace: company.team
flowId: multiplecondition_flow_a
flow_b:
type: io.kestra.plugin.core.condition.ExecutionFlow
namespace: company.team
flowId: multiplecondition_flow_b

After

triggers:
- id: multiple_listen_flow
type: io.kestra.plugin.core.trigger.Flow
dependsOn:
- flowId: multiplecondition_flow_a
namespace: company.team
states: [SUCCESS]
- flowId: multiplecondition_flow_b
namespace: company.team
states: [SUCCESS]
window:
every: P1D

The arbitrary string keys (flow_a, flow_b) are dropped; dependsOn is always a list. The windowAdvance property is removed with no direct equivalent.

Namespace-wide alerting (prefix matching)

Before

triggers:
- id: alert_on_failure
type: io.kestra.plugin.core.trigger.Flow
conditions:
- type: io.kestra.plugin.core.condition.ExecutionStatus
in:
- FAILED
- WARNING
- type: io.kestra.plugin.core.condition.ExecutionNamespace
namespace: company
comparison: PREFIX

After

triggers:
- id: alert_on_failure
type: io.kestra.plugin.core.trigger.Flow
dependsOn:
- states: [FAILED, WARNING]
when: "{{ namespace | startsWith('company') }}"

namespace in dependsOn is an exact match. Use when with startsWith for prefix matching.

Label-based filtering

Before

triggers:
- id: after_prod
type: io.kestra.plugin.core.trigger.Flow
conditions:
- type: io.kestra.plugin.core.condition.ExecutionStatus
in: [SUCCESS]
- type: io.kestra.plugin.core.condition.ExecutionLabels
labels:
env: production

After

triggers:
- id: after_prod
type: io.kestra.plugin.core.trigger.Flow
dependsOn:
- namespace: company.team
labels:
env: production
states: [SUCCESS]

Conditional filtering with expressions

Before

triggers:
- id: after_extract
type: io.kestra.plugin.core.trigger.Flow
preconditions:
id: my_filter
where:
- id: flow1
filters:
- field: NAMESPACE
type: STARTS_WITH
value: io.kestra.tests
- field: EXPRESSION
type: IS_TRUE
value: "{{ labels.some == 'label' }}"

After

triggers:
- id: after_extract
type: io.kestra.plugin.core.trigger.Flow
dependsOn:
- when: "{{ namespace | startsWith('io.kestra.tests') }}"
states: [SUCCESS]
labels:
some: label

labels handles exact key-value matching declaratively. when handles everything else.

Filtering on upstream execution outputs

Before

triggers:
- id: after_extract
type: io.kestra.plugin.core.trigger.Flow
conditions:
- type: io.kestra.plugin.core.condition.ExecutionOutputs
expression: "{{ outputs.row_count > 0 }}"

After

triggers:
- id: after_extract
type: io.kestra.plugin.core.trigger.Flow
dependsOn:
- flowId: extract
namespace: company.team
when: "{{ outputs.row_count > 0 }}"

Filtering on retry attempts

Before

triggers:
- id: after_flaky
type: io.kestra.plugin.core.trigger.Flow
conditions:
- type: io.kestra.plugin.core.condition.HasRetryAttempt

After

triggers:
- id: after_flaky
type: io.kestra.plugin.core.trigger.Flow
dependsOn:
- flowId: flaky_pipeline
namespace: company.team
states: [SUCCESS]
when: "{{ hasRetryAttempt == true }}"

Negation: trigger on any state except SUCCESS

Before

triggers:
- id: on_non_success
type: io.kestra.plugin.core.trigger.Flow
conditions:
- type: io.kestra.plugin.core.condition.Not
conditions:
- type: io.kestra.plugin.core.condition.ExecutionStatus
in: [SUCCESS]

After (option 1: explicit states)

triggers:
- id: on_non_success
type: io.kestra.plugin.core.trigger.Flow
dependsOn:
- flowId: extract
namespace: company.team
states: [FAILED, WARNING, KILLED, CANCELLED]

After (option 2: when expression)

triggers:
- id: on_non_success
type: io.kestra.plugin.core.trigger.Flow
dependsOn:
- flowId: extract
namespace: company.team
when: "{{ state != 'SUCCESS' }}"

Mixed triggers: success and failure on the same upstream flow

Before

triggers:
- id: on_completion
type: io.kestra.plugin.core.trigger.Flow
states: [SUCCESS]
conditions:
- type: io.kestra.plugin.core.condition.ExecutionFlow
namespace: company.team
flowId: flow_a
- id: on_failure
type: io.kestra.plugin.core.trigger.Flow
states: [FAILED]
preconditions:
id: flowsFailure
flows:
- namespace: company.team
flowId: flow_a
states: [FAILED]

After

triggers:
- id: on_completion
type: io.kestra.plugin.core.trigger.Flow
dependsOn:
- flowId: flow_a
namespace: company.team
states: [SUCCESS]
- id: on_failure
type: io.kestra.plugin.core.trigger.Flow
dependsOn:
- flowId: flow_a
namespace: company.team
states: [FAILED]

Same dependsOn syntax regardless of whether the original used conditions or preconditions.

Passing outputs downstream

Flow trigger outputs are now scoped by flow ID. The path format is trigger.outputs.<flowId>.<outputKey>.

Before (flat map, all upstream outputs merged together)

triggers:
- id: after_extract
type: io.kestra.plugin.core.trigger.Flow
inputs:
date: "{{ trigger.outputs.date }}"
preconditions:
id: flows
flows:
- namespace: company.team
flowId: extract
states: [SUCCESS]

After (scoped by flow ID)

triggers:
- id: after_extract
type: io.kestra.plugin.core.trigger.Flow
inputs:
date: "{{ trigger.outputs.extract.date }}"
dependsOn:
- flowId: extract
namespace: company.team

For multi-flow triggers, each upstream flow’s outputs are accessed under its own key:

dependsOn:
- flowId: stg_sales
namespace: company.team
- flowId: stg_marketing
namespace: company.team

Access as {{ trigger.outputs.stg_sales.row_count }} and {{ trigger.outputs.stg_marketing.row_count }}.

ForEachItem chain

When using Flow triggers to chain ForEachItem child flows, reference the child flow’s outputs using its flowId:

Before

triggers:
- id: 01_complete
type: io.kestra.plugin.core.trigger.Flow
inputs:
testFile: "{{ trigger.outputs.myFile }}"
preconditions:
id: output_01_success
flows:
- namespace: io.kestra.tests.trigger.foreachitem
flowId: flow-trigger-for-each-item-child
states: [SUCCESS]

After

triggers:
- id: 01_complete
type: io.kestra.plugin.core.trigger.Flow
inputs:
testFile: "{{ trigger.outputs.flow-trigger-for-each-item-child.myFile }}"
dependsOn:
- flowId: flow-trigger-for-each-item-child
namespace: io.kestra.tests.trigger.foreachitem

mode: OR and N-of-M logic

The mode property controls how dependsOn entries are combined when evaluating whether to fire.

ValueBehaviorRequired properties
ALL (default)Fires when all dependsOn entries are satisfied
ANYFires as soon as any one entry is satisfied
AT_LEASTFires when at least minSatisfied entries are satisfiedminSatisfied (integer ≥ 1, ≤ entry count)

OR logic: fire when any upstream completes

Previously, OR logic required N separate Flow triggers. mode: ANY consolidates them into one.

Before (two separate triggers)

triggers:
- id: on_salesforce
type: io.kestra.plugin.core.trigger.Flow
conditions:
- type: io.kestra.plugin.core.condition.ExecutionFlow
namespace: company.sources
flowId: ingest_salesforce
- type: io.kestra.plugin.core.condition.ExecutionStatus
in: [SUCCESS]
- id: on_hubspot
type: io.kestra.plugin.core.trigger.Flow
conditions:
- type: io.kestra.plugin.core.condition.ExecutionFlow
namespace: company.sources
flowId: ingest_hubspot
- type: io.kestra.plugin.core.condition.ExecutionStatus
in: [SUCCESS]

After

triggers:
- id: react_to_any_source
type: io.kestra.plugin.core.trigger.Flow
mode: ANY
dependsOn:
- flowId: ingest_salesforce
namespace: company.sources
states: [SUCCESS]
- flowId: ingest_hubspot
namespace: company.sources
states: [SUCCESS]

mode: ANY fires as soon as either dependency is satisfied. The default mode: ALL requires every entry to be satisfied before the trigger fires.

OR logic with a time window

triggers:
- id: daily_any_source
type: io.kestra.plugin.core.trigger.Flow
mode: ANY
dependsOn:
- flowId: ingest_salesforce
namespace: company.sources
- flowId: ingest_hubspot
namespace: company.sources
window:
deadline: "09:00:00"

Fire before 9 AM when either source completes.

N of M: at least 2 out of 3

triggers:
- id: partial_success
type: io.kestra.plugin.core.trigger.Flow
mode: AT_LEAST
minSatisfied: 2
dependsOn:
- flowId: ingest_salesforce
namespace: company.sources
states: [SUCCESS]
- flowId: ingest_hubspot
namespace: company.sources
states: [SUCCESS]
- flowId: ingest_zendesk
namespace: company.sources
states: [SUCCESS]
window:
deadline: "09:00:00"

mode: AT_LEAST fires when minSatisfied entries are satisfied. minSatisfied must be ≥ 1 and ≤ the number of dependsOn entries.

What replaces what

Old property / condition typeNew equivalent
conditions list on Flow triggerdependsOn list
preconditions blockdependsOn list + window
multipleConditions blockdependsOn list + window.every
ExecutionStatus (in: [SUCCESS])states: [SUCCESS] on the dependsOn entry
ExecutionFlow (flowId, namespace)flowId + namespace on the dependsOn entry
ExecutionNamespace (exact)namespace on the dependsOn entry
ExecutionNamespace (comparison: PREFIX)when: "{{ namespace | startsWith('...') }}" on the entry
ExecutionLabels (labels: {k: v})labels: {k: v} on the dependsOn entry
ExecutionOutputs (expression)when with outputs.<key> on the entry
HasRetryAttemptwhen: "{{ hasRetryAttempt == true }}" on the entry
Not > ExecutionStatusExplicit states list or when: "{{ state != 'SUCCESS' }}"
Multiple triggers for OR logicmode: ANY with dependsOn entries
preconditions.resetOnSuccess: trueremove it, this is the only behavior in 2.0
timeWindow.type: DAILY_TIME_DEADLINEwindow.deadline
timeWindow.type: DAILY_TIME_WINDOWwindow.from + window.to
timeWindow.type: DURATION_WINDOWwindow.every
timeWindow.type: SLIDING_WINDOWwindow.lookback

Window configuration

The window property applies to Flow triggers and controls how Kestra accumulates upstream executions before evaluating dependsOn entries. Set exactly one property group per window; combining groups is a validation error.

Window typePropertiesBehavior
Deadlinedeadline: "09:00:00"Upstream flows must complete by a fixed time each day
Daily time rangefrom: "06:00:00" + to: "12:00:00"Only executions within a daily time range count
Fixed intervalevery: P1D + optional offset: PT6HRecurring window of a fixed size, offset from midnight
Lookbacklookback: PT1HRolling window looking back from the current evaluation time

None of the window types changes how often the trigger fires. Once every dependsOn entry has been satisfied and an execution has been created, the stored results are reset, so every dependency has to be satisfied again before another execution is created.

Deadline

window:
deadline: "09:00:00"

Daily time range

window:
from: "06:00:00"
to: "12:00:00"

Fixed interval

window:
every: P1D
offset: PT6H

Lookback

window:
lookback: PT1H

Replacing timeWindow types

Old timeWindow.typeNew window property
DAILY_TIME_DEADLINEdeadline: "09:00:00"
DAILY_TIME_WINDOWfrom: "06:00:00" + to: "12:00:00"
DURATION_WINDOWevery: P1D + optional offset: PT6H
SLIDING_WINDOWlookback: PT1H

preconditions.resetOnSuccess: true can be removed, since resetting after firing is the only behavior in 2.0. resetOnSuccess: false has no equivalent; use mode: ANY if you want an execution to be created as soon as any single upstream flow succeeds.

Behavior changes after upgrading

Silent failures → FAILED executions

Previously, if an expression on a Flow trigger failed to render (for example, because an upstream output key did not exist), the trigger silently dropped the event and no execution was created. In Kestra 2.0, a FAILED execution is created instead, making failures visible in the UI and actionable via downstream alerting.

No migration action is required. Review your Flow trigger inputs expressions to ensure they reference valid output keys and avoid unexpected FAILED executions after upgrading.

State store reset and in-flight events

Previously, auto-generated condition keys (condition_1, condition_2, …) meant that reordering entries could reset accumulated window state. In Kestra 2.0, dependsOn entry keys are derived from each entry’s namespace and flowId, making them order-independent.

The trigger-level state store key also changes: the old scheme used preconditions.id; the new scheme uses {flowId}/{triggerId}. Existing accumulated state from preconditions will not be found after upgrading; in-flight multi-flow triggers re-evaluate from scratch. For most deployments this means at most one missed trigger cycle.

Old-format events in the async queue are discarded gracefully (logged as a warning). No user action is required.

Migration steps

  1. Replace conditions: on all triggers with a when: Pebble expression. This applies to Schedule, Webhook, HTTP, and any other trigger type that used conditions.
  2. Replace conditions: and preconditions: on Flow triggers with dependsOn: entries and (if applicable) window:.
  3. Check dependsOn states values. When omitted, states defaults to all terminal states and PAUSED. Add states explicitly on any entry that should match only specific states.
  4. Update trigger.outputs references in multi-flow triggers from trigger.outputs.<key> to trigger.outputs.<flowId>.<key>. Single-flow triggers can keep the unscoped form.
  5. Update timeWindow to window using the property mapping table above.
  6. Validate by saving updated flows in the Kestra UI or via the API and confirming they parse without errors.

Was this page helpful?