Filter Remote Resources and Open Console Views
Start with the run/action distinction
When you are investigating a remote execution, begin with flyte.remote.Run: it represents the top-level task run. The run contains a nested flyte.remote.Action, and a run's actions form a linearly nested execution tree. That distinction matters when a run is present but the useful failure or work item is one of its child actions.
Initialize Flyte before listing or retrieving remote resources. Both Run.listall() and Action.listall() call ensure_client() before making a RunService request. For example, the SDK's remote setup and synchronous listing can be used as follows:
import flyte
import flyte.remote as remote
flyte.init_passthrough("endpoint")
with remote.auth_metadata(("key1", "value1")):
print(remote.Run.listall(limit=2))
flyte.init_from_config(), flyte.init(), and other supported initialization paths provide the client and the project/domain defaults used by the remote APIs. Without an endpoint or API key, client initialization raises MissingEndpointOrApiKeyError.
Find runs with Run.listall()
Use Run.listall() when you need to search across runs in the configured project and domain. It is syncified: normal synchronous use returns an iterator, while the asynchronous form is available through the generated .aio() interface.
import flyte.models
import flyte.remote as remote
training_runs = remote.Run.listall(
in_phase=(flyte.models.ActionPhase.SUCCEEDED,),
task_name="image_finetune_training.finetune_image_model",
sort_by=("created_at", "desc"),
limit=1,
)
training_run = next(training_runs)
outputs = training_run.outputs()
This is the pattern used by examples/ml/image_classification/batch_inference.py: select the newest successful run for a task, take its outputs, and use them in a later run.
Run filters and scope
Run.listall() accepts the following arguments:
| Argument | Behavior |
|---|---|
in_phase | A tuple of ActionPhase values or strings. One phase becomes an equality filter; multiple phases become a VALUE_IN filter. String values are converted using ACTION_PHASE_{value.upper()}. |
task_name | Adds an equality filter on task_name. |
task_version | Adds an equality filter on task_version. |
created_by_subject | Adds an equality filter on created_by. This is the creator's subject, not a username. |
sort_by | A (field, direction) tuple, with direction "asc" or "desc". The shared sorting helper defaults to ("created_at", "asc"). |
limit | Maximum total number of runs yielded; the default is 100. |
project, domain | Override the globally configured project or domain for this listing. |
created_at | A TimeFilter applied to run creation time. |
updated_at | A TimeFilter applied to last-update time. |
For example, phase, task, version, and creator filters are combined by the SDK before the request is sent:
runs = remote.Run.listall(
in_phase=("running", "failed"),
task_name="my_task",
task_version="v1.0",
created_by_subject="user:alice",
sort_by=("updated_at", "desc"),
limit=20,
project="customer-us-east",
domain="production",
)
Prefer flyte.models.ActionPhase values when possible. A string is looked up as a protobuf enum name; an invalid phase string therefore surfaces as a protobuf enum conversion error rather than becoming an arbitrary server-side value.
If project or domain is omitted, Run.listall() reads the corresponding value from get_init_config(). The explicit values win, as in the focused filter tests: project="customer-us-east" changes the request's project identifier, while omission uses the initialized default.
The method calls run_service.list_runs with protobuf filters and paginates using the response token. Each request asks for min(100, limit) items. The method still enforces the caller's total limit after yielding results, so a limit larger than 100 can span multiple pages. The server's lack of a continuation token ends iteration.
The CLI's flyte get run command forwards the same filters. Its --only-mine option obtains remote.User.get().subject() and passes that value as created_by_subject; its date options become TimeFilter instances.
Narrow a run to its actions
Once you have a run name, call Action.listall(for_run_name=...). Actions support phase, creation-time, update-time, and sorting filters, but not run-list filters such as task name, task version, or creator:
running_actions = list(
remote.Action.listall(
for_run_name=run_name,
in_phase=(flyte.models.ActionPhase.RUNNING,),
sort_by=("created_at", "asc"),
)
)
Action.listall() defaults to ("created_at", "asc") when sort_by is omitted. It requests 100 actions per page and continues until the service returns no token. Unlike Run.listall(), it has no limit parameter, so a large action tree is not capped by the SDK call.
For one action, use its run name and action name:
action = remote.Action.get(run_name=run_name, name=action_name)
The CLI follows the same split: flyte get action calls Action.get(run_name=..., name=...) when an action name is supplied, and otherwise calls Action.listall(for_run_name=...). The configured project and domain are used to build the action identifier.
An action may not correspond to a task metadata record. Action.task_name returns the task name when the action metadata contains a task ID, and otherwise returns None. The root action is commonly named a0; the SDK's live-abort example deliberately ignores it while looking for a child action:
import flyte.models
import flyte.remote as remote
actions = list(
remote.Action.listall(
for_run_name=r.name,
in_phase=[flyte.models.ActionPhase.RUNNING],
)
)
for a in actions:
if a.name != "a0":
a.abort("External abort!!")
break
Bound searches by time
Construct flyte.remote.TimeFilter with optional datetime bounds. after is inclusive and before is exclusive:
from datetime import datetime, timezone
created_at = remote.TimeFilter(
after=datetime(2024, 1, 1, tzinfo=timezone.utc),
before=datetime(2024, 2, 1, tzinfo=timezone.utc),
)
runs = remote.Run.listall(
in_phase=("succeeded",),
created_at=created_at,
updated_at=remote.TimeFilter(
after=datetime(2024, 1, 15, tzinfo=timezone.utc)
),
)
time_filtering() converts these bounds into list-service protobuf filters: after becomes GREATER_THAN_OR_EQUAL, and before becomes LESS_THAN. Each bound is normalized with astimezone(timezone.utc).isoformat(), and both bounds are added when present. An empty TimeFilter() produces no filters. Use timezone-aware datetimes—preferably UTC—because the helper calls astimezone() and the CLI explicitly attaches UTC to naive date values before constructing its filters.
Time filters compose with phase and other run filters, and the same created_at/updated_at behavior is used by Action.listall().
Inspect and control the selected resource
A returned Run exposes name, a string phase, and raw_phase, which is the underlying protobuf enum. A returned Action exposes name, run_name, task_name, phase as an ActionPhase enum, raw_phase, action_id, and start_time as a UTC-aware datetime.
Fetch remote data lazily when you need more than the list response:
details = run.details()
inputs = run.inputs()
outputs = run.outputs()
For live investigation, run.wait() delegates to its nested action and copies the updated action protobuf back into the Run. run.watch() similarly yields action-detail updates while synchronizing the run state. On an action, watch() can stop when the action is running, when logs are ready, or when it reaches a terminal state; wait() provides the corresponding progress display and stopping behavior.
Use get_logs() to iterate log lines or show_logs() to display them. Both action methods may wait until logs are ready and default to the latest attempt. Run.get_logs() and Run.show_logs() delegate to the nested action.
run.abort(reason=...) calls the run abort RPC; action.abort(reason=...) calls the action abort RPC. Both treat a NOT_FOUND ConnectRPC error as harmless and propagate other RPC errors. done() reports terminal-state status. Do not treat sync() as a refresh operation: Run.sync() and Action.sync() currently return the existing object as placeholders.
Open console views while investigating
Run.url gives you a browser link for the containing run:
flyte.init_from_config()
r = flyte.run(main, 10, 30.0)
print(r.url)
The property delegates to the initialized client's Console.run_url() with the run's project, domain, and name. Action has no direct url property. Use the containing run URL for an action investigation, or use the client's console to build a task/resource link. Task.url similarly uses client.console.task_url(...).
The Console constructor takes the configured endpoint and insecure flag. Direct construction is useful when you need a resource link explicitly:
from flyte.remote._client.controlplane import Console
console = Console("https://example.com")
run_url = console.run_url(
project="myproject",
domain="development",
run_name="run123",
)
task_url = console.task_url(
project="myproject",
domain="development",
task_name="mytask",
)
The generated path has this shape:
{scheme}://{domain}/v2/domain/{domain}/project/{project}/{resource}/{resource_name}
Thus the examples produce URLs such as https://example.com/v2/domain/development/project/myproject/runs/run123 and https://example.com/v2/domain/development/project/myproject/tasks/mytask. Console also provides app_url() and trigger_url(); trigger resources use {task_name}/{trigger_name} as the resource name.
ClientSet creates the configured Console and exposes it as client.console, which is the console used by Run.url and Task.url. Endpoint normalization chooses HTTPS by default and HTTP when insecure=True. DNS-style endpoints such as dns:///example.com are reduced to example.com. The local-development special case maps localhost:8090 to the web console at localhost:8080, preserving the selected HTTP/HTTPS scheme:
console = Console("dns:///localhost:8090", insecure=True)
assert console._http_domain == "http://localhost:8080"
The URL builder interpolates resource names directly into the path and does not URL-quote them. The source also marks console URL configuration as a future TODO, so the endpoint-derived path is the behavior to rely on today rather than a separately configurable console base URL.
Investigation checklist for common surprises
- Initialize the client before calling
Run.listall(),Action.listall(),Run.get(), orAction.get(). - Remember that run listing defaults to the initialized project/domain, while
project=anddomain=override them. - Use
ActionPhasevalues or valid phase strings; run phases are exposed as strings, while action phases are enum values. - Expect run pagination and a default run limit of 100. Action listing pages at 100 but has no caller-side limit.
- Use timezone-aware UTC bounds for
TimeFilter;afterincludes its instant andbeforeexcludes its instant. - A manually constructed
Runmust contain anactionfield in its protobuf or initialization raisesRuntimeError("Run does not have an action"). - For child-action investigation, filter
Action.listall()by phase and account for the root actiona0; actions can also lack a task name. insecure=Truechanges generated browser links tohttp://; it is not only an RPC transport setting.- Treat a missing resource during abort as a no-op only for
NOT_FOUND; other errors are raised.