Troubleshooting components (Components v2)

<<../../../concepts/components/_v2_banner.md>>

This document contains tips for troubleshooting the following kinds of problems when using the component framework:

Got an error when trying to use a capability from the namespace

Sometimes, when connecting to a capability such as a protocol, service, or directory in your namespace, the channel returns an error when you try to use it. For example, consider the following snippet:

use fuchsia_component::client;
use log::info;
...
let echo = client::connect_to_service::<fidl_fuchsia_echo::EchoMarker>().expect("error connecting to echo");
if let Some(err) = echo.echo_string(Some("Hippos rule!")).await {
    info!("Echo failed: {}", err);
}

In this Rust example, the code connects to the Echo protocol in the namespace through the usual means, by calling the connect_to_service API in the fuchsia_component crate. This call should succeed as long as the protocol was mapped into the component‘s namespace by a use declaration in the component’s manifest:

use: [
    { protocol: "/svc/fuchsia.echo.Echo" },
    ...
],

However, when the connect_to_service call returns successfully, it does not necessarily mean the protocol will be available. If it's not available, the usual symptom is that a call to the protocol over the channel fails. The snippet above checks for this and logs the error.

There are a few conditions that can cause these errors:

Channel was closed after connecting to a capability in the namespace

When a protocol or service is opened in the namespace, or a directory in the namespace is used for the first time, component manager will perform capability routing to find the source of the capability. It‘s possible that routing will fail if one of the component manifests in the routing path was configured incorrectly. For example, it’s possible that an offer or expose declaration is missing from some component in the path, or one of the components in the chain could not be resolved.

There are a couple ways to check if a routing failure was the cause of channel closure:

  • Check for an epitaph on the closed channel.
  • Check the component manager logs with fx log --only component_manager

See checking a closed channel for details on how to check if a channel was closed and get an epitaph if there was one. Normally, the epitaph set for a routing failure is ZX_ERR_UNAVAILABLE.

For a more detailed description of the error, check the kernel debuglog. Look for a message beginning with ERROR: Failed to route that contains the requesting component's moniker. This error should give you a hint about what went wrong. Example:

 [component_manager] ERROR: Failed to route protocol `/svc/fuchsia.echo.Echo`
 from component `/core:0/echo_client:0`: A `use from realm` declaration was
 found at `/echo_client:0` for `/svc/fuchsia.echo.Echo`, but no matching
 `offer` declaration was found in the parent

Depending on where the component runs the log may be tagged as belonging to the component, for example [my_component] instead of [component_manager]. For a self-contained example of failed routing that demonstrates the content of this section, refer to //examples/components/routing_failed.

Component fails to start

It‘s possible that the capability was routed successfully, but something went wrong when the runner tried to start the component. Here’s a couple ways this can happen:

  • The program declaration was misconfigured. For example, the binary's path was spelled incorrectly.
  • The binary or some other resource needed to start the component was not included in its package.

When this happens, the runner closes the channel with a PEER_CLOSED status, with no epitaph. See checking a closed channel for details on how to check if a channel was closed and get an epitaph if there was one.

Note that just from the state of the channel, it's impossible to distinguish whether the runner failed to start the component, or the component terminated or closed the channel itself.

For a more detailed description of the error, check the logs. The log to check depends on the runner:

  • For the ELF runner, check the component manager logs with fx log --only component_manager
  • For other runners, check the logs of the runner component. You can do this by running fx log --tag <runner-name>.

The form of the error message is runner-dependent. For the ELF runner, look for a message starting with ERROR: Failed to start component:

[component_manager] ERROR: Failed to start component
`fuchsia-pkg://fuchsia.com/components-routing-failed-example#meta/echo_server_bad.cm`: unable to
load component with url
"fuchsia-pkg://fuchsia.com/components-routing-failed-example#meta/echo_server_bad.cm": error
loading executable: "reading object at "bin/routing_failed_echo_server_oops" failed: A FIDL
client's channel was closed: PEER_CLOSED"

In this case, the component failed to start because its binary was not present.

For an example of a component that failed to start due to a misconfigured component manifest, refer to //examples/components/routing_failed.

Component terminated or closed the channel

If you have verified that routing succeeded and the component started successfully, then the final possibility is that the source component closed the channel itself. This can happen while the component was running, or can be a side effect of the component terminating.

If the component terminated because it crashed, you can look for a crash report in fx log that starts like this:

[00177.191] 01775:02371> crashsvc: exception received, processing
[00177.191] 01775:02371> <== fatal : process echo_client.cm[21090] thread initial-thread[21092]
<stack trace follows...>

Note that you'll see name of the component manifest in the dump (this is actually the process name).

If the component closed the channel itself, there‘s no universal way to debug if this happened. You can look in the component’s logs, or in the case of a protocol capability, search the source code for the name of the source code in a language-appropriate format. For example, for the fuchsia.Echo protocol in Rust, you might search for a use statement for fidl_fuchsia_echo, then follow the identifier to where it's used.

The final possibility is that a component may have already been started by a previous capability request, but has since terminated on its own.

Checking if a channel was closed

If a protocol channel was closed, you'll normally notice when trying to make a call on it, if the call is awaited on. For example:

let res = echo.echo_string(Some("Hippos rule!")).await;
match res {
    Ok(_) => { info!("Call succeeded!"); }
    Err(fidl::Error::ClientChannelClosed { status, service_name } => {
        error!("Channel to service {} was closed with status: {}", service_name, status);
    }
    Err(e) => {
        error!("Unexpected error: {}", e);
    }
};

If the call doesn‘t return a value (i.e. it is a one-way method), you’ll only get an error if the channel was closed prior to the call. However, if your protocol pipelines a call that does return a value, you can also check that:

let (echo_resp, echo_resp_svc) = fidl::endpoints::create_proxy();
let res = echo_async.echo_string_pipelined(Some("Hippos rule!"), echo_resp_svc);
match res {
    Ok(_) => {
        info!("EchoString succeeded!");
    }
    Err(fidl::Error::ClientChannelClosed { status, service_name } => {
        error!("Channel to service {} was closed with status: {}", service_name, status);
    }
    Err(e) => {
        error!("Unexpected error: {}", e);
    }
};
let res = echo_resp.get_result().await;
match res {
    Ok(_) => { info!("GetResult succeeded!"); }
    Err(fidl::Error::ClientChannelClosed { status, service_name } => {
        error!("Channel to service {} was closed with status: {}", service_name, status);
    }
    Err(e) => {
        error!("Unexpected error: {}", e);
    }
};

If echo_resp is closed, it‘s likely that’s indirectly because echo_async was closed.

In the case of routing failure, component manager sets an epitaph on the channel that was opened through the namespace. You can get the epitaph on a closed channel as follows:

let stream = echo.take_event_stream();
match stream.next().await {
    Some(Err(fidl::Error::ClientChannelClosed { status, .. })) => {
        info!("Echo channel was closed with epitaph, probably due to \
              failed routing: {}", status);
    }
    Some(m) => {
        info!("Received message other than epitaph or peer closed: {:?}", m);
    }
    None => {
        info!("Component failed to start or Echo channel was closed by server");
    }
}

Note: in the echo_async example, the epitaph would be set on echo_async, not echo_resp.

Test does not start

A Components v2 test is written using the Test Runner Framework. Sometimes, if one of the test components is configured incorrectly, this can result in the test failing to run.

If this happens, you'll see an error like the following from fx test:

Test suite encountered error trying to run tests: getting test cases
Caused by:
    The test protocol was closed. This may mean `fuchsia.test.Suite` was not configured correctly.
    Refer to: https://fuchsia.dev/fuchsia-src/development/components/v2/troubleshooting#troubleshoot-test

Misconfigurations can happen in a few test-specific ways:

If you're still seeing the same error after trying the preceding solutions, consider following the troubleshooting steps for using capabilities. The troubleshooting steps may help fix issues from routing the fuchsia.test.Suite capability in integration tests.

The test failed to expose fuchsia.test.Suite to test manager

This happens when the test root fails to expose fuchsia.test.Suite from the test root. The simple fix is to add an expose declaration:

// test_root.cml
expose: [
    ...
    {
        protocol: "/svc/fuchsia.test.Suite",
        from: "self",  // If a child component is the test driver, put `from: "#driver"`
    },
],

The test driver failed to expose fuchsia.test.Suite to the root

If the test driver and test root are different components, the test driver must also expose fuchsia.test.Suite to its parent, the test root.

Make sure this is in the driver's CML:

// test_driver.cml
expose: [
    ...
    {
        protocol: "/svc/fuchsia.test.Suite",
        from: "self",
    },
],

If this is the problem, you can expect to see an error like this in the logs:

ERROR: Failed to route protocol `/svc/fuchsia.test.Suite` from component
`/test_manager:0/...`: An `expose from #driver` declaration was found at `/test_manager:0/...`
for `/svc/fuchsia.test.Suite`, but no matching `expose` declaration was found in the child

The test driver does not use a test runner

The test driver must use the appropriate test runner corresponding to the language and test framework the test is written with. For example, the driver of a Rust test needs the following declaration:

// test_driver.cml
include: [ "src/sys/test_runners/rust/default.shard.cml" ]

Also, if the test driver is a child of the [test root][trf-test-root], you need to offer it to the driver:

// test_root.cml
offer: [
    {
        runner: "rust_test_runner",
        to: [ "#driver" ],
    },
],