> This page is for version 5.0 (Latest) (default).
> For other versions, use one of these documentation indexes:
> - 5.0 (Latest) (default): https://docs.nvidia.com/holoscan/sdk-user-guide/5-0-latest/llms.txt
> - ltsb-2.0: https://docs.nvidia.com/holoscan/sdk-user-guide/ltsb-2-0/llms.txt
> - 4.6: https://docs.nvidia.com/holoscan/sdk-user-guide/4-6/llms.txt
> - 4.5: https://docs.nvidia.com/holoscan/sdk-user-guide/4-5/llms.txt
> - 4.4: https://docs.nvidia.com/holoscan/sdk-user-guide/4-4/llms.txt
> - 4.3: https://docs.nvidia.com/holoscan/sdk-user-guide/4-3/llms.txt
> - Archives: https://docs.nvidia.com/holoscan/sdk-user-guide/archives/llms.txt

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.nvidia.com/holoscan/sdk-user-guide/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.nvidia.com/holoscan/sdk-user-guide/_mcp/server.

Quick answers to the questions we hear most often about Holoscan SDK 5.x —
from developers arriving fresh and from developers arriving from 4.x, where
many familiar names (Application, schedulers, Conditions, GXF) no longer
exist. Each answer is deliberately short and links to the page that owns the
full story.

If you are migrating from 4.x, start with
[Migrating from 4.x](/holoscan/sdk-user-guide/introduction/migrating-from-4x);
if you are new to the SDK, start with
[Core concepts](/holoscan/sdk-user-guide/getting-started/core-concepts).

## Naming and concepts

### Why is the method called `contract()`?

Because it declares a *temporal contract* between your operator and the
runtime: what the runtime must satisfy **before** it invokes `compute()`. A
contract names the trigger (what causes an activation, such as `OnEach{camera}`
or `OnClock{.period = 33ms}`), the input selection (which samples participate,
such as `LatestWithin{.max_age = 5ms}`), and the missing-data policy (what
happens when an input is absent or stale, such as `ReuseLast` or `Skip`). It
is a contract in the everyday sense — both sides agree on terms up front, the
compiler checks them, and the runtime enforces them — which is what lets
`compute()` stay entirely oblivious to timing. See
[Temporal contracts](/holoscan/sdk-user-guide/building-operators/temporal-contracts).

### Why errors-as-values instead of exceptions?

Fallible runtime APIs return `holoscan::expected<T, Error>` instead of
throwing, for four reasons: failure is visible in the signature (you cannot
forget to handle it — the results are `[[nodiscard]]`); returning an error is
an ordinary value return with deterministic cost, whereas stack unwinding is
expensive and hard to bound on real-time paths; a code-only `Error` (say,
`kNotReady` on a hot polling path) allocates nothing; and value-channel code
works even in builds with exceptions disabled. Exceptions still exist at
genuine boundaries — graph *authoring* throws on programming errors, by
design. Branch on `ErrorCode`, and use `HOLOSCAN_TRY` to propagate concisely.
See [Errors as values](/holoscan/sdk-user-guide/getting-started/errors-as-values).

### Where did the schedulers go?

There is no user-selectable scheduler in 5.x — no Greedy, Multi-Thread, or
Event-Based Scheduler to choose between. Instead, each operator's
`contract()` declares *when* it should run, `compile()` lowers those
declarations into an execution plan, and the runtime's dispatcher executes
that plan. Scheduling intent moved from a global runtime knob into a
per-operator, statically checkable declaration. See
[Core concepts](/holoscan/sdk-user-guide/getting-started/core-concepts) and
[Temporal contracts](/holoscan/sdk-user-guide/building-operators/temporal-contracts).

### Why is there no `Application` class?

5.x separates the three phases that `Application` used to bundle. You
**compose** a `holoscan::Graph` (add operators with `graph.op<T>(...)`, wire
them with `add_flow`), **compile** it with `holoscan::compile(graph)` into an
`ExecutionPlan`, and **run** the plan with `holoscan::run_async(plan)`, which
returns a `RunSession` you control. Each phase has its own failure style:
authoring throws, `compile()` returns a plan you check with `plan.ok()`, and
a run fails closed. Because the phases are separate values, you can compile
once and run many times, inspect a rejected plan's diagnostics, or hold
multiple sessions. See
[Composing a graph](/holoscan/sdk-user-guide/getting-started/composing-a-graph)
and [Running and stopping](/holoscan/sdk-user-guide/getting-started/running-and-stopping).

### What replaced Conditions (`CountCondition`, `PeriodicCondition`, ...)?

Triggers and selectors inside `contract()`. A 4.x `PeriodicCondition` becomes
`OnClock{.period = ...}`; message-availability conditions become the default
`OnAnyInput{}` or an explicit `OnEach{port}`; alignment of multiple inputs
becomes `OnAligned{...}`. Unlike Conditions, these are typed value objects
that the compiler validates as part of the plan rather than runtime components
attached to an operator. See
[Temporal contracts](/holoscan/sdk-user-guide/building-operators/temporal-contracts).

### What are the "three declarations" of an operator?

`setup()` declares structure (ports and parameters), `contract()` declares
time (trigger, selection, missing-data policy), and `compute()` does the work
— nothing else. Keeping the three concerns in three methods is what makes
operators testable and reusable across graphs with different timing. See
[Operator anatomy](/holoscan/sdk-user-guide/building-operators/operator-anatomy).

## Practical

### Why does my run fail with a clock-binding diagnostic?

Ordinary input- and clock-triggered pipelines must select a default clock.
If it is absent, `compile()` rejects the plan with `TEMPORAL_CLOCK_UNDECLARED`.
Declare and select it before compiling:

```cpp
graph.set_default_clock(graph.add_clock<holoscan::RealtimeClock>("clock"));
```

Python can use `hs.Graph("pipeline", default_clock=hs.RealtimeClock)`.
Readiness-only sources may be clock-free. `RUNTIME_CLOCK_BINDING_UNAVAILABLE`
instead means an admitted plan's required clock could not be bound at startup.
See [Time and clocks](/holoscan/sdk-user-guide/getting-started/time-and-clocks).

### Why am I getting `kResourceExhausted`?

Because 5.x is bounded by construction: tensor pools are preallocated, queues
have declared depths, and per-compute emit counts are finite. When a bounded
resource has no free capacity, the SDK does not silently allocate more — it
returns `Error{kResourceExhausted}` so you can react (drop, retry, or treat
it as a real sizing bug). The usual causes are a pool sized too small for the
pipeline's in-flight depth, or a slot held longer than intended (for example,
a tensor loan that was never committed). See
[Understanding failures](/holoscan/sdk-user-guide/debugging-and-testing/understanding-failures)
and [Payload type rules](/holoscan/sdk-user-guide/data-and-payloads/payload-type-rules).

### Why can't I use `int` as a port type?

Because `int` (like `size_t` and `long`) is a platform-dependent alias, and a
port type is a wire contract that must mean the same thing everywhere. The
in-process scalar catalog is a closed set of fixed-width types: `bool`,
`uint8_t` through `int64_t`, `float`, and `double`. Write
`holoscan::Input<std::int32_t>`, not `holoscan::Input<int>`. Structured types
go through schema payloads, and tensors through `Tensor` ports — not through
ad-hoc structs. See
[Payload type rules](/holoscan/sdk-user-guide/data-and-payloads/payload-type-rules).

### How do I synchronize CUDA streams between operators?

You don't — not on the hot path. Stream ordering is automatic: committing a
tensor records its readiness, and a `Tensor` you receive is already ordered on
your compute stream, so you launch kernels immediately.

> **Warning**
>
> **Never call `cudaStreamSynchronize` (or `cudaDeviceSynchronize`) on the hot
> path.** It stalls the pipeline and defeats the SDK's asynchronous ordering.
> If you need CPU access to tensor data, request host access explicitly — that
> is the sanctioned, visible synchronization point.

See [Tensors 101](/holoscan/sdk-user-guide/getting-started/tensors-101) and
[Tensors in depth](/holoscan/sdk-user-guide/data-and-payloads/tensors-in-depth).

### How do I stop a running pipeline cleanly?

`run_async(plan)` returns a `RunSession`; call `session.request_stop()` and
then `session.wait()`. The session's `termination()` tells you how the run
ended (for example `RunTermination::kStopped` for a requested stop). There is
no deadlock-based shutdown idiom as in 4.x — stopping is an explicit,
first-class request. See
[Running and stopping](/holoscan/sdk-user-guide/getting-started/running-and-stopping)
and [Restart control](/holoscan/sdk-user-guide/advanced/restart-control).

### How does an operator finish the whole run successfully?

Inside `compute()`, call `context.request_completion()`. The session reports
`RunTermination::kRequestedCompleted`, distinct from natural finite
`kCompleted`, and blocking `run()` returns zero. The request is immediate: it
closes new callback admission for the whole session, but does not guarantee
EOS, source closure, downstream drain, or queued delivery. Never retain
`ExecutionContext`; it is valid only for the callback.

There is no `kRequestedStopped` because `kStopped` already denotes the explicit
stop category; HSDK 5 has no separate natural-stop success state. It also has
no operator-local deactivate/readiness-removal API or deadlock-as-success rule.
Count-bounded EOS and drain semantics are separate from this request. See
[Running and stopping](/holoscan/sdk-user-guide/getting-started/running-and-stopping).

### Why does `compile()` return a rejected plan instead of throwing?

A rejected graph is an expected outcome, not an exceptional one — so
`compile()` reports it as a value. Check `plan.ok()` and, when it is false,
read `plan.diagnostics()`: each diagnostic names what was rejected (an invalid
trigger/selector combination, over-subscribed queue capacity, a type mismatch
across a flow) before anything runs. Failing at compile time, with a full
list of problems, beats failing one bug at a time at runtime. See
[Compile and check](/holoscan/sdk-user-guide/getting-started/compile-and-check).

### Can I use the channel client API inside my graph?

No.

> **Warning**
>
> **One graph, one vocabulary.** Inside a graph, operators communicate through
> typed ports (`Input<T>`/`Output<T>`) wired with `add_flow()` — the runtime
> materializes the channels for you. The `ChannelRuntime` client API exists
> only for standalone processes outside any graph. Never mix the two: using
> `ChannelRuntime` inside an operator bypasses the compiled plan and its
> guarantees.

See [Interprocess channels](/holoscan/sdk-user-guide/advanced/interprocess-channels)
and [Standalone channel clients](/holoscan/sdk-user-guide/advanced/standalone-channel-clients).

## Scope: languages, platforms, operators

### Is Python supported?

Yes. Holoscan SDK 5.x provides a Python 3.12+ application-authoring API for
defining Python operators, composing them with registered C++ operators,
compiling inspectable plans, and controlling run sessions. It includes reusable
graphs, graph policy, graph-owned YAML/JSON configuration, scalar and
inline-schema external topics, Tensor interop, typed exceptions, registry
proxies, and typing stubs.

Python operators subclass `holoscan.Operator` and declare typed ports, a
temporal contract, and a `compute()` callback. The current surface does not
expose C++ managed-signal ownership. Python applications import registered
operator providers and manage process signals themselves. Start with the
[Python guide](/holoscan/sdk-user-guide/python) and use the
[Python migration guide](/holoscan/sdk-user-guide/introduction/migrating-python-from-4x)
rather than inferring compatibility from 4.x packages.

### Which platforms are supported?

Linux on x86_64 and arm64 with NVIDIA GPUs. The authoritative, per-release
platform and CUDA matrix lives on
[SDK installation](/holoscan/sdk-user-guide/setup/sdk-installation) — check
that page rather than assuming a 4.x-era matrix carries over.

### Where are the domain operators — inference, visualization, video I/O?

In [Holoscan Modules](/holoscan/sdk-user-guide/modules). 5.x separates the
core runtime — the graph/compile/run machinery, temporal contracts, payloads,
tensors, and channels — from domain operators, which ship as separately
selectable modules on top of it. This release carries three core modules:
[Inference](/holoscan/sdk-user-guide/modules/inference) (`InferenceOp`),
[Vision](/holoscan/sdk-user-guide/modules/vision) (nine CV-CUDA and NPP operators),
and [Visualization](/holoscan/sdk-user-guide/modules/visualization)
(`HolovizOp`). For other capabilities, author your own operators against the core API or
look for a community module on
[HoloHub](/holoscan/sdk-user-guide/resources/holohub).

### What is the difference between a Holoscan Core Module and a Holoscan External Module?

Both are libraries that extend the SDK with operators, subgraphs, and/or schema. Core modules live in the
SDK repository, are released on the SDK cadence as separate, optional Debian packages and
Python wheels, and are supported through the SDK release process. External modules live in a
separate project, are published by the community or by hardware partners on their own cadence,
and are supported by their maintainers. See
[Core modules and ecosystem modules](/holoscan/sdk-user-guide/modules#core-modules-and-ecosystem-modules).

### How do I publish my own module?

Scaffold it from the Holoscan CLI module template
(`holoscan create my-module`), follow the HoloHub
*Create a Holoscan module* tutorial, and list it in the HoloHub Modules
catalog. If you want to propose a module for the SDK itself, read the
[core module development guide](https://github.com/nvidia-holoscan/holoscan-sdk/blob/v5.0.0/modules/DEVELOPER.md)
first. See [Build your own module](/holoscan/sdk-user-guide/modules#build-your-own-external-holoscan-module).

### Is there anything like distributed applications / fragments?

Cross-process pipelines are built from interprocess channels: processes
exchange typed payloads over named external topics rather than partitioning
one application object into fragments. See
[External topics](/holoscan/sdk-user-guide/composing-applications/external-topics)
and [Interprocess channels](/holoscan/sdk-user-guide/advanced/interprocess-channels).

## Migration

### I have a 4.x application. Where do I start?

Read [Migrating from 4.x](/holoscan/sdk-user-guide/introduction/migrating-from-4x)
first — it maps each 4.x concept (Application, Fragment, Conditions,
schedulers, `make_operator`) to its 5.x counterpart and walks a real port.
Then work through the getting-started sequence from
[Core concepts](/holoscan/sdk-user-guide/getting-started/core-concepts):
the mental model (compose → compile → run, errors as values, contracts) is
worth absorbing before you translate code line by line.

### Do my 4.x operators or GXF extensions work in 5.x?

No. 5.x is a new runtime, not an API revision — there is no GXF underneath,
no entity/codelet layer, and no compatibility shim for 4.x operator classes.
Operators are re-authored around the three declarations (`setup()` /
`contract()` / `compute()`), which is usually a simplification: scheduling
Conditions become contract triggers, and receive/emit boilerplate becomes
typed `Input<T>`/`Output<T>` calls returning `expected`. See
[Migrating from 4.x](/holoscan/sdk-user-guide/introduction/migrating-from-4x)
and [Operator anatomy](/holoscan/sdk-user-guide/building-operators/operator-anatomy).

### Where should I ask a question this page doesn't answer?

Check the [Glossary](/holoscan/sdk-user-guide/resources/glossary) for
unfamiliar terms, the
[Understanding failures](/holoscan/sdk-user-guide/debugging-and-testing/understanding-failures)
page for any diagnostic the runtime printed, and the
[Holoscan by example](/holoscan/sdk-user-guide/examples/holoscan-by-example)
tour for working code to compare against.