Connect components

This document demonstrates how to connect components together using capabilities and additional tools for parent components to manage their children.

Concepts

You should understand the following concepts before continuing with this guide:

  • The Component Framework assembles the namespace for a component using component declarations that describe the capabilities the component requires to function. The capabilities the component exposes to others are assembled into an exposed directory .
  • Every component receives a handle to the server end of a Directory channel called the outgoing directory . The component's executable makes discoverable any capabilities that it provides through this directory.
  • At runtime, the component instance tree connects individual component instances together in a hierarchy of parent and child relationships. The component instance tree and the capability routes over that tree are collectively referred to as the component topology .
  • Parent components declare child components either statically in their component manifest or dynamically using a component collection . A collection is a container for dynamic children that may be created and destroyed at runtime using the fuchsia.component.Realm framework protocol.

For more details on these concepts, see Realms and Capabilities.

Connecting capabilities

Components interact with each other through their capabilities. Capabilities implemented in a component need to be declared in that component's manifest and routed through its parent/child components. Other components that use that capability also need to declare their use in their manifests. This capability routing describes which component should act as the provider for any given client. Once the proper components are identified, the component manager initiates connections between components.

Provide a capability implementation

Components that implement a capability must declare the implementation in their component manifest using a capabilities declaration.

See the following example that declares a FIDL protocol capability in the providing component's manifest:

{
    // ...

    // Capabilities provided by this component.
    capabilities: [
        { protocol: "fidl.examples.routing.echo.Echo" },
    ],
    expose: [
        {
            protocol: "fidl.examples.routing.echo.Echo",
            from: "self",
        },
    ],
}

At runtime, the provider component provides an implementation of the capability by serving it through the outgoing directory using the fuchsia.io protocol. The generated FIDL bindings wrap this handle and enable the provider to begin receiving incoming requests:

Rust

// Wrap protocol requests being served.
enum IncomingRequest {
    Echo(EchoRequestStream),
}

#[fuchsia::main(logging = false)]
async fn main() -> Result<(), anyhow::Error> {
    let mut service_fs = ServiceFs::new_local();

    // Initialize inspect
    component::health().set_starting_up();
    inspect_runtime::serve(component::inspector(), &mut service_fs)?;

    // Serve the Echo protocol
    service_fs.dir("svc").add_fidl_service(IncomingRequest::Echo);
    service_fs.take_and_serve_directory_handle().context("failed to serve outgoing namespace")?;

    // Component is serving and ready to handle incoming requests
    component::health().set_ok();

    // Attach request handler for incoming requests
    service_fs
        .for_each_concurrent(None, |request: IncomingRequest| async move {
            match request {
                IncomingRequest::Echo(stream) => handle_echo_request(stream).await,
            }
        })
        .await;

    Ok(())
}

// Handler for incoming service requests
async fn handle_echo_request(mut stream: EchoRequestStream) {
    while let Some(event) = stream.try_next().await.expect("failed to serve echo service") {
        let EchoRequest::EchoString { value, responder } = event;
        responder.send(value.as_ref().map(|s| &**s)).expect("failed to send echo response");
    }
}

C++

// Handler for incoming service requests
class EchoImplementation : public fidl::examples::routing::echo::Echo {
 public:
  void EchoString(fidl::StringPtr value, EchoStringCallback callback) override { callback(value); }
  fidl::examples::routing::echo::Echo_EventSender* event_sender_;
};

int main(int argc, const char** argv) {
  async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);
  auto context = sys::ComponentContext::CreateAndServeOutgoingDirectory();

  // Initialize inspect
  sys::ComponentInspector inspector(context.get());
  inspector.Health().StartingUp();

  // Serve the Echo protocol
  EchoImplementation echo_instance;
  fidl::Binding<fidl::examples::routing::echo::Echo> binding(&echo_instance);
  echo_instance.event_sender_ = &binding.events();
  fidl::InterfaceRequestHandler<fidl::examples::routing::echo::Echo> handler =
      [&](fidl::InterfaceRequest<fidl::examples::routing::echo::Echo> request) {
        binding.Bind(std::move(request));
      };
  context->outgoing()->AddPublicService(std::move(handler));

  // Component is serving and ready to handle incoming requests
  inspector.Health().Ok();

  return loop.Run();
}

Connect to routed capabilities

Client components request capabilities in their component manifest with a use declaration.

See the following example of a client component's manifest that uses the FIDL protocol provided by the previous component:

{
    // ...

    // Capabilities used by this component.
    use: [
        { protocol: "fidl.examples.routing.echo.Echo" },
    ],
}

At runtime, the client component connects to the capability handles populated in its namespace using the fuchsia.io protocol. The Fuchsia component library works with the generated FIDL bindings to wrap these handles and provide a structured interface for communicating over the channel:

Rust

#[fuchsia::main]
async fn main() -> Result<(), anyhow::Error> {
    // Parse arguments, removing binary name
    let mut args: Vec<String> = std::env::args().collect();
    args.remove(0);

    // Connect to FIDL protocol
    let echo = connect_to_protocol::<EchoMarker>().expect("error connecting to echo");

    // Send messages over FIDL interface
    for message in args {
        let out = echo.echo_string(Some(&message)).await.expect("echo_string failed");
        tracing::info!("Server response: {}", out.as_ref().expect("echo_string got empty result"));
    }

    Ok(())
}

C++

int main(int argc, const char* argv[], char* envp[]) {
  // Set tags for logging.
  syslog::SetTags({"echo_client"});

  // Connect to FIDL protocol
  fidl::examples::routing::echo::EchoSyncPtr echo_proxy;
  auto context = sys::ComponentContext::Create();
  context->svc()->Connect(echo_proxy.NewRequest());

  // Send messages over FIDL interface for each argument
  fidl::StringPtr response = nullptr;
  for (int i = 1; i < argc; i++) {
    ZX_ASSERT(echo_proxy->EchoString(argv[i], &response) == ZX_OK);
    if (!response.has_value()) {
      FX_SLOG(INFO, "echo_string got empty result");
    } else {
      FX_SLOG(INFO, "Server response", KV("response", response->c_str()));
    }
  }

  return 0;
}

Route capabilities

Components may only access capabilities routed to them. Capabilities can originate from anywhere in the component topology as long as a valid capability route exists as a chain of the following declarations from the capability provider to any consumers:

  • expose: Routes a capability up to the component's parent.
  • offer: Routes a capability down to one of the component's children.

To connect capability providers with components requesting those capabilities, do the following:

  1. Add an offer or expose declaration to the capability provider component:

    {
        // ...
    
        // Capabilities provided by this component.
        capabilities: [
            { protocol: "fidl.examples.routing.echo.Echo" },
        ],
        expose: [
            {
                protocol: "fidl.examples.routing.echo.Echo",
                from: "self",
            },
        ],
    }
    
  2. For each intermediate component in the component instance tree, include additional expose and offer declarations until you reach the consuming component containing a use declaration:

    {
        // Two children: a server and client.
        children: [
            {
                name: "echo_server",
                url: "#meta/echo_server.cm",
            },
            {
                name: "echo_client",
                url: "#meta/echo_client.cm",
            },
        ],
        offer: [
            // Route Echo protocol from server to client.
            {
                protocol: "fidl.examples.routing.echo.Echo",
                from: "#echo_server",
                to: "#echo_client",
            },
    
            // Route LogSink protocol to both children.
            {
                protocol: "fuchsia.logger.LogSink",
                from: "parent",
                to: [
                    "#echo_client",
                    "#echo_server",
                ],
            },
        ],
    }
    

Managing child components

Components can interact with each other from anywhere in the component topology through capabilities as long as a valid capability route exists between them. There are additional methods that enable parent components to interact with their direct children.

The following example component declares a single static child named lifecycle and a collection named echo where additional child components may be created at runtime:

{
    // ...

    // Statically declared child components
    children: [
        {
            name: "lifecycle",
            url: "#meta/lifecycle.cm",
        },
    ],

    // Collection to hold dynamically created child components
    collections: [
        {
            name: "echo",
            durability: "transient",
        },
    ],

    // Capabilities required by this component
    use: [
        {
            protocol: "fuchsia.component.Binder",
            from: "#lifecycle",
        },
        {
            protocol: "fuchsia.component.Realm",
            from: "framework",
        },
    ],

    // Capabilities required by child components
    offer: [
        {
            protocol: "fuchsia.logger.LogSink",
            from: "parent",
            to: [
                "#echo",
                "#lifecycle",
            ],
        },
    ],
}

Notice that a collection behaves like a static child instance in the parent component's manifest — you can give it a name and offer specific capabilities to it. All child components in the collection may access the set of capabilities offered to it.

Start child components

The Component Framework provides the fuchsia.component.Binder protocol for parent components to explicitly start a child that may not expose any other capabilities. Since this capability is provided by the framework, child components only need to expose it from their component manifest:

{
    // ...

    // Capabilities exposed from this component to parent.
    expose: [
        {
            // Expose this protocol so that parent component can start it
            // by binding to this capability.
            protocol: "fuchsia.component.Binder",
            from: "framework",
        },
    ],
}

Create dynamic children

To create a new child component at runtime, use the fuchsia.component.Realm protocol to create the component inside of an existing collection. Call the CreateChild method with the following parameters:

  • CollectionRef: Describes the collection where the component will be added.
  • Child: Component declaration, including its name and component URL.

Rust

use fidl_fuchsia_component::{BinderMarker, CreateChildArgs, RealmMarker};
use fidl_fuchsia_component_decl::{Child, ChildRef, CollectionRef, StartupMode};

// ...

// Use the fuchsia.component.Realm protocol to create a dynamic
// child instance in the collection.
async fn create_dynamic_child() {
    let realm = client::connect_to_protocol::<RealmMarker>()
        .expect("failed to connect to fuchsia.component.Realm");

    let mut collection_ref = CollectionRef { name: String::from("echo") };
    let child_decl = Child {
        name: Some(String::from("lifecycle_dynamic")),
        url: Some(String::from("#meta/echo_server.cm")),
        startup: Some(StartupMode::Lazy),
        ..Child::EMPTY
    };

    realm
        .create_child(&mut collection_ref, child_decl, CreateChildArgs::EMPTY)
        .await
        .expect("create_child failed")
        .expect("failed to create child");
}

C++

#include <fuchsia/component/cpp/fidl.h>
#include <fuchsia/component/decl/cpp/fidl.h>

// ...

// Use the fuchsia.component.Realm protocol to create a dynamic
// child instance in the collection.
void CreateDynamicChild() {
  fuchsia::component::decl::CollectionRef collection_ref = {
      .name = "echo",
  };
  fuchsia::component::decl::Child child_decl;
  child_decl.set_name("lifecycle_dynamic");
  child_decl.set_url("#meta/echo_server.cm");
  child_decl.set_startup(fuchsia::component::decl::StartupMode::LAZY);

  realm_proxy_->CreateChild(std::move(collection_ref), std::move(child_decl),
                            fuchsia::component::CreateChildArgs(),
                            [&](fuchsia::component::Realm_CreateChild_Result result) {
                              ZX_ASSERT(!result.is_err());
                              FX_LOGS(INFO) << "Dynamic child instance created.";

                              ConnectDynamicChild();
                            });
}

Connect to child capabilities

Because the parent of a dynamic component is not known at build time, its exposed capabilities cannot be named in capability routes expressed in the component manifest.

To connect with the capabilities exposed by a dynamic child instance:

  1. Use the fuchsia.component.Realm protocol to open the child's exposed directory. Call the OpenExposedDir method with the child component's name and the collection name:

    Rust

    use fidl_fuchsia_component::{BinderMarker, CreateChildArgs, RealmMarker};
    use fidl_fuchsia_component_decl::{Child, ChildRef, CollectionRef, StartupMode};
    
    // ...
    // Connect to the fidl.examples.routing.echo capability exposed by the child
    // instance, and send a request.
    async fn connect_dynamic_child(message: String) {
        // Open the child's exposed directory
        let exposed_dir = client::open_childs_exposed_directory(
            String::from("lifecycle_dynamic"),
            Some(String::from("echo")),
        )
        .await
        .expect("failed to open exposed directory");
    
        // ...
    }
    

    C++

    #include <fuchsia/component/cpp/fidl.h>
    #include <fuchsia/component/decl/cpp/fidl.h>
    
    // ...
    
    // Use the fuchsia.component.Realm protocol to open the exposed directory of
    // the dynamic child instance.
    void ConnectDynamicChild() {
      fuchsia::component::decl::ChildRef child_ref = {
          .name = "lifecycle_dynamic",
          .collection = "echo",
      };
    
      fidl::InterfaceHandle<fuchsia::io::Directory> exposed_dir;
      realm_proxy_->OpenExposedDir(
          child_ref, exposed_dir.NewRequest(),
          [this, exposed_dir = std::move(exposed_dir)](
              fuchsia::component::Realm_OpenExposedDir_Result result) mutable {
            ZX_ASSERT(!result.is_err());
            std::shared_ptr<sys::ServiceDirectory> svc = std::make_shared<sys::ServiceDirectory>(
                sys::ServiceDirectory(std::move(exposed_dir)));
    
            SendEchoRequest(svc);
          });
    }
    
  2. Connect to the child's exposed capabilities using the exposed directory handle as the root:

    Rust

    // Access the fidl.examples.routing.echo capability provided there
    let echo = client::connect_to_protocol_at_dir_root::<EchoMarker>(&exposed_dir)
        .expect("failed to connect to fidl.examples.routing.echo");
    
    let response = echo
        .echo_string(Some(&message))
        .await
        .expect("echo_string failed")
        .expect("echo_string got empty result");
    info!("Server response: {}", response);
    

    C++

    // Connect to the fidl.examples.routing.echo capability exposed by the child's
    // service directory.
    void SendEchoRequest(std::shared_ptr<sys::ServiceDirectory> svc_directory) {
      // Connect to the protocol inside the child's exposed directory
      svc_directory->Connect(echo_proxy_.NewRequest());
    
      // Send a protocol request
      echo_proxy_->EchoString(message_, [&](fidl::StringPtr response) {
        FX_LOGS(INFO) << "Server response: " << response;
        DestroyDynamicChild();
      });
    }
    

Destroy dynamic children

When the dynamic child is no longer needed, use the fuchsia.component.Realm protocol to destroy the component instance. Call the DestroyChild method with a ChildRef representing the child inside the collection.

Rust

use fidl_fuchsia_component::{BinderMarker, CreateChildArgs, RealmMarker};
use fidl_fuchsia_component_decl::{Child, ChildRef, CollectionRef, StartupMode};

// ...

// Use the fuchsia.component.Realm protocol to destroy the dynamic
// child instance running in the collection.
async fn destroy_dynamic_child() {
    let realm = client::connect_to_protocol::<RealmMarker>()
        .expect("failed to connect to fuchsia.component.Realm");

    let mut child_ref = ChildRef {
        name: String::from("lifecycle_dynamic"),
        collection: Some(String::from("echo")),
    };

    realm
        .destroy_child(&mut child_ref)
        .await
        .expect("destroy_child failed")
        .expect("failed to destroy child");
}

C++

#include <fuchsia/component/cpp/fidl.h>
#include <fuchsia/component/decl/cpp/fidl.h>

// ...

// Use the fuchsia.component.Realm protocol to destroy the dynamic
// child instance running in the collection.
void DestroyDynamicChild() {
  fuchsia::component::decl::ChildRef child_ref = {
      .name = "lifecycle_dynamic",
      .collection = "echo",
  };

  realm_proxy_->DestroyChild(std::move(child_ref),
                             [&](fuchsia::component::Realm_DestroyChild_Result result) {
                               ZX_ASSERT(!result.is_err());
                               FX_LOGS(INFO) << "Dynamic child instance destroyed.";

                               // Terminate the loop
                               loop_->Quit();
                             });
}

This causes the component to stop if it is currently running. To handle this event in your component, see listen for stop events.

Controlling component lifecycle

The Component Framework provides features to modify and interact with various parts of the component lifecycle.

For more details on lifecycle concepts, see Component lifecycle.

Lifecycle notifications

The ELF runner notifies components of lifecycle events using the fuchsia.process.lifecycle.Lifecycle protocol.

To listen for stop notifications in your child component:

  1. Subscribe to the lifecycle event in your component manifest:

    Rust

    // Information about the program to run.
    program: {
        // Use the built-in ELF runner.
        runner: "elf",
    
        // The binary to run for this component.
        binary: "bin/lifecycle_example_rust",
    
        // Subscribe to component lifecycle events
        lifecycle: { stop_event: "notify" },
    },
    

    C++

    // Information about the program to run.
    program: {
        // Use the built-in ELF runner.
        runner: "elf",
    
        // The binary to run for this component.
        binary: "bin/lifecycle_example_cpp",
    
        // Subscribe to component lifecycle events
        lifecycle: { stop_event: "notify" },
    },
    
  2. Register a lifecycle handler using the startup handle provided by the runner:

    Rust

    use fidl_fuchsia_process_lifecycle::{LifecycleRequest, LifecycleRequestStream};
    
    // ...
    
    #[fuchsia::main(logging_tags = ["lifecycle", "example"])]
    async fn main() {
        // Take the lifecycle handle provided by the runner
        match fuchsia_runtime::take_startup_handle(HandleInfo::new(HandleType::Lifecycle, 0)) {
            Some(lifecycle_handle) => {
                info!("Lifecycle channel received.");
                // Begin listening for lifecycle requests on this channel
                let x: fuchsia_zircon::Channel = lifecycle_handle.into();
                let async_x = AsyncChannel::from(
                    fuchsia_async::Channel::from_channel(x).expect("Async channel conversion failed."),
                );
                let mut req_stream = LifecycleRequestStream::from_channel(async_x);
                info!("Awaiting request to close...");
                while let Some(request) =
                    req_stream.try_next().await.expect("Failure receiving lifecycle FIDL message")
                {
                    match request {
                        LifecycleRequest::Stop { control_handle: c } => {
                            info!("Received request to stop. Shutting down.");
                            c.shutdown();
                            process::exit(0);
                        }
                    }
                }
    
                // We only arrive here if the lifecycle channel closed without
                // first sending the shutdown event, which is unexpected.
                process::abort();
            }
            None => {
                // We did not receive a lifecycle channel, exit abnormally.
                error!("No lifecycle channel received, exiting.");
                process::abort();
            }
        }
    }
    

    C++

    #include <fuchsia/process/lifecycle/cpp/fidl.h>
    
    // ...
    
    // Implementation of the fuchsia.process.lifecycle FIDL protocol
    class LifecycleHandler : public fuchsia::process::lifecycle::Lifecycle {
     public:
      explicit LifecycleHandler(async::Loop* loop) : loop_(loop) {
        // Get the PA_LIFECYCLE handle, and instantiate the channel with it
        zx::channel channel = zx::channel(zx_take_startup_handle(PA_LIFECYCLE));
        // Bind to the channel and start listening for events
        bindings_.AddBinding(
            this, fidl::InterfaceRequest<fuchsia::process::lifecycle::Lifecycle>(std::move(channel)),
            loop_->dispatcher());
        FX_LOGS(INFO) << "Lifecycle channel received.";
      }
    
      // This is the Stop event we must override - see the pure virtual function we
      // need to override at the declaration of fuchsia::process::lifecycle::Lifecycle
      void Stop() override {
        FX_LOGS(INFO) << "Received request to stop. Shutting down.";
        // Shut down our loop - it's important to call Shutdown() here vs. Quit()
        loop_->Shutdown();
        // Close the binding
        bindings_.CloseAll();
      }
    
     private:
      async::Loop* loop_;
      fidl::BindingSet<fuchsia::process::lifecycle::Lifecycle> bindings_;
    };
    

Start with parent

Component manifests let you mark a child as eager, which causes the component framework to implicitly start that child with the parent.

If the eager child fails to start for any reason (such as a missing component), component manager exhibits the following behavior:

  • If the parent is not the root component, the parent will start but the component that bound to it will observe a dropped connection (just like any other failed binding).
  • If the parent is the root component, component manager will crash, with an error message like:

    [component_manager] ERROR: Failed to route protocol `fuchsia.appmgr.Startup` with target component `/startup`:
    failed to resolve "fuchsia-pkg://fuchsia.com/your_component#meta/your_component.cm":
    package not found: remote resolver responded with PackageNotFound
    

Components marked as eager can cause system crashes when they are not present if their ancestors are also marked eager up to the root component. This is important because many build configurations create system images containing a subset of the available components. To avoid this problem, declare these components using core realm shards to ensure they can be safely excluded from test builds and product images.

An eager component should also be in the same package set as its parent since the component will be started at the same time as its parent. Typically, eager components should be in the product's base package set.

To determine if your package is in the base package set, run the following command:

fx list-packages --verbose my-package

This command outputs a list of the package sets where the matching package is found. For example, system-update-checker is in the base and universe package sets:

$ fx list-packages --verbose system-update-checker
system-update-checker [base universe]

You can also look at all the packages in the base package set using the --base option:

fx list-packages --base

Reboot on terminate

Component manifests let you control the termination policy of your component using on_terminate. Components with the "reboot-on-terminate" policy set cause the system to gracefully reboot if the component terminates for any reason (including successful exit).

To enable this feature, do the following:

  1. Mark the child as on_terminate: reboot in the parent's component manifest:

    // core.cml
    {
        children: [
            ...
            {
                name: "system-update-checker",
                url: "fuchsia-pkg://fuchsia.com/system-update-checker#meta/system-update-checker.cm",
                startup: "eager",
                on_terminate: "reboot",
            },
        ],
    }
    
  2. Add the component's moniker to component manager's security policy allowlist at //src/security/policy/component_manager_policy.json5:

    // //src/security/policy/component_manager_policy.json5
    {
        security_policy: {
            ...
            child_policy: {
                reboot_on_terminate: [
                    ...
                    "/core/system-update-checker",
                ],
            },
        },
    }
    

Troubleshooting

This section contains common issues you may encounter trying to use and connect to capabilities from your component.

When component connections fail, the underlying FIDL channel closes. FIDL protocol bindings return an error status if the channel was closed. Consider the following example:

Rust

let echo = connect_to_protocol::<EchoMarker>()
    .context("Failed to connect to echo service")?;
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);
    }
};

C++

fuchsia::examples::EchoPtr echo_proxy;
auto context = sys::ComponentContext::Create();
context->svc()->Connect(echo_proxy.NewRequest());

// Sets an error handler that will be called if an error causes the underlying 
// channel to be closed. 
echo_proxy.set_error_handler([&loop](zx_status_t status) { 
  printf("Channel was closed with status: %d\n", status); 
  // ... 
}); 

echo_proxy->EchoString("Hippos rule!", [&](std::string response) {
  // ...
});

To determine the underlying cause of a channel closure, you can inspect the optional epitaph set on the channel. To retrieve the epitaph on a closed channel, do the following:

Rust

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

C++

echo_proxy.set_error_handler([&loop](zx_status_t status) {
  // If an Epitaph was present on the channel, its error value will be passed as
  // the parameter.
  printf("Channel was closed with epitaph: %d\n", status);
});

Capability routing failed

Component manager performs capability routing to find the source of a given capability once your component attempts to access the capability. Routing can fail if one of the component manifests in the routing path is configured incorrectly. For example, 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.

Do the following to check if a routing failure was the cause of channel closure:

  • Check the component logs with ffx log for a message beginning with Failed to route that explains where the routing chain failed. For example:

    [echo_client][][W] Failed to route protocol
    `fidl.examples.routing.echo.Echo` with target component `/core/ffx-laboratory:echo_realm/echo_client`:
    A `use from parent` declaration was found at `/core/ffx-laboratory:echo_realm/echo_client`
    for `fidl.examples.routing.echo.Echo`, but no matching `offer` declaration was found in the parent
    
  • Check for an epitaph on the closed channel. Normally, the epitaph set for a routing failure is ZX_ERR_UNAVAILABLE:

    [echo_client][][I] Connecting to Echo protocol failed with error
    "A FIDL client's channel to the service fidl.examples.routing.echo.Echo was closed: UNAVAILABLE"
    

For a self-contained example of failed capability routing, see //examples/components/routing_failed.

Component failed to start

You may encounter an error if capability routing was successful, but an issue occurred resolving or starting the component. The form of the error message depends on the component runner:

  • For the ELF runner, check the component manager logs with ffx log --filter component_manager. Look for a message starting with Failed to start component. For example:

    [component_manager] WARN: 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 to the service (anonymous) File was closed: PEER_CLOSED"
    
  • For other runners, check the logs of the runner component. You can do this by running the following command:

    ffx log --tags runner-name
    

To address the issue, verify the following:

  • The program declaration in your component manifest is configured properly. For example, verify that the binary's path is spelled correctly.
  • The binary itself and all other resource needed to start the component are included in the package.

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

Component terminated or closed the channel

If you have verified that routing succeeded and the component started successfully, you may be experiencing an issue where 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 ffx log that contains the component name in the dump:

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

If the source component closed the channel itself, here are some tips to further troubleshoot the cause:

  • Refer to the source component's logs for error messages.
  • Use ffx debug fidl to examine the FIDL connection traffic with fidlcat for errors or unexpected behavior.