Skip to content

execution_timeout on @task.stub tasks is not enforced #74716

Open

Description

@Andrushika

Under which category would you file this issue?

Task SDK

Apache Airflow version

main

What happened and how to reproduce it?

A @task.stub task is declared in a Python Dag and runs in a language SDK. Its execution_timeout is set on the Python stub and is serialized with the Dag. But the runtime never gets it. StartupDetails, TIRunContext and TaskInstance in the supervisor schema have no timeout field. So the runtime cannot start a timer or send SetExecutionTimeout, and the task runs past the timeout.

A Python task does not have this problem because the task runner parses the Dag file and reads task.execution_timeout itself (task_runner.py).

Steps to reproduce (Go)

  1. Pack a Go bundle with one handler that sleeps 90 seconds and logs every 5 seconds.
  2. Add a Dag with this task as @task.stub(queue="golang", execution_timeout=timedelta(seconds=10)), and a Python task with the same timeout as a control.
  3. In breeze shell --backend postgres, start the api-server, dag-processor and scheduler with LocalExecutor, using the same coordinator config as the Go SDK e2e test. Trigger the Dag.
Dag and Go handler
@task.stub(queue="golang", execution_timeout=timedelta(seconds=10))
def go_sleep(): ...


@task(execution_timeout=timedelta(seconds=10))
def py_sleep():
    for i in range(18):
        time.sleep(5)
        print(f"still running {(i + 1) * 5}s")


@dag(dag_id="timeout_repro", schedule=None, start_date=datetime(2026, 1, 1), catchup=False)
def timeout_repro():
    go_sleep()
    py_sleep()
func goSleep(actx airflow.Context) error {
	start := time.Now()
	for i := 1; i <= 18; i++ {
		select {
		case <-actx.Done():
			actx.Logger().InfoContext(actx, "context cancelled", "elapsed_s", time.Since(start).Seconds())
			return actx.Err()
		case <-time.After(5 * time.Second):
			actx.Logger().InfoContext(actx, "still running", "elapsed_s", time.Since(start).Seconds())
		}
	}
	actx.Logger().InfoContext(actx, "finished after full sleep", "elapsed_s", time.Since(start).Seconds())
	return nil
}

// registered as airflow.TaskHandler("timeout_repro", "go_sleep", goSleep)

Result

task serialized execution_timeout final state ran for
py_sleep (Python) 10.0 failed, AirflowTaskTimeout about 13s
go_sleep (Go stub) 10.0 success about 93s

I got the same result in two runs. I only ran Go. The other language SDKs get the same StartupDetails, so I think they behave the same, but I did not run them.

What you think should happen instead?

The task should fail when it runs past execution_timeout, like a Python task, with its retries applied.

Operating System

No response

Deployment

None

Apache Airflow Provider(s)

No response

Versions of Apache Airflow Providers

No response

Official Helm Chart version

Not Applicable

Kubernetes Version

No response

Helm Chart configuration

No response

Docker Image customizations

No response

Anything else?

The runtime would need the timeout from the server, for example as a new field in TIRunContext. That would be an Execution API change, so it is bigger than the native Dag case.

Are you willing to submit PR?

  • Yes I am willing to submit a PR!

Code of Conduct

Activity

  1. added
    kind:bugThis is a clearly a bug
    needs-triagelabel for new issues that we didn't triage yet
    on Oct 14, 2026
  2. Andrushika commented on Oct 14, 2026

    @Andrushika
    ContributorAuthor

    Triage: area:task-sdk, area:java-sdk, area:go-sdk, area:ts-sdk

  3. 0xamlab commented on Oct 14, 2026

    @0xamlab

    Confirmed the gap locally: TIRunContext has 14 model fields and the supervisor schema JSON lists none named execution_timeout; StartupDetails embeds that context at comms.py:422, so the native runtime never receives the timeout. The only place that sends SetExecutionTimeout is the Python task runner at task_runner.py:2264, which stub tasks bypass. The serialized DAG does carry execution_timeout on the stub, but it stops at the API boundary because TIRunContext (_generated.py:981) omits the field.

  4. ChickenBenny commented on Oct 16, 2026

    @ChickenBenny

    Hi, I’d like to work on this~

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    kind:bugThis is a clearly a bugneeds-triagelabel for new issues that we didn't triage yet

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions