Google is committed to advancing racial equity for Black communities. See how.

FIDL language specification

This document is a specification of the Fuchsia Interface Definition Language (FIDL) syntax.

For more information about FIDL's overall purpose, goals, and requirements, see Overview.

Also, see a modified EBNF description of the FIDL grammar.


FIDL provides a syntax for declaring named bits, constants, enums, structs, tables, unions, and protocols. These declarations are collected into libraries for distribution.

FIDL declarations are stored in plain text UTF-8 files. Each file consists of a sequence of semicolon-delimited declarations. The order of declarations within a FIDL file, or among FIDL files within a library, is irrelevant. FIDL does not require (or support) forward declarations of any kind.



FIDL comments start with two (//) or three (///) forward slashes, continue to the end of the line, and can contain UTF-8 content (which is, of course, ignored). The three-forward-slash variant is a "documentation comment", and causes the comment text to be emitted into the generated code (as a comment, escaped correctly for the target language).

// this is a comment
/// and this one is too, but it also ends up in the generated code
struct MyStruct { // plain comment
    int32 f; // as is this one
}; // and this is the last one!

Note that documentation comments can also be provided via the [Doc] attribute.


The following are keywords in FIDL.

as, bits, compose, const, enum, library, protocol,
resource, struct, table, union, using, xunion.


FIDL identifiers label declarations and their members. FIDL identifiers must match the regex [a-zA-Z]([a-zA-Z0-9_]*[a-zA-Z0-9])?. In words: identifiers must start with a letter, can contain letters, numbers, and underscores, but cannot end with an underscore.

// a struct named "Foo"
struct Foo { };

// an enum named "enum", containing a single member
enum enum { WITH_A_MEMBER = 1; };

FIDL library names label FIDL libraries. FIDL library names consist of one or more elements each matching the regex [a-z][a-z0-9]*. In words: library name elements must start with a lowercase letter, can contain lowercase letters, and numbers (they cannot contain uppercase letters, nor underscores). Library names are used in Qualified Identifiers.

// a library named "foo"
library foo;

Identifiers and library names are case-sensitive.

Qualified Identifiers

FIDL always looks for unqualified symbols within the scope of the current library. To reference symbols in other libraries, they must be qualified by prefixing the identifier with the library name or alias thereof.


library objects;
using textures as tex;

protocol Frob {
    // "Thing" refers to "Thing" in the "objects" library
    // "tex.Color" refers to "Color" in the "textures" library
    Paint(Thing thing, tex.Color color);

struct Thing {
    string name;


library textures;

struct Color {
    uint32 rgba;


FIDL supports integer, floating point, boolean, string, and enumeration literals, using a simplified syntax familiar to C programmers (see below for examples).


FIDL supports the following constant types: bits, booleans, signed and unsigned integers, floating point values, strings, and enumerations. The syntax is similar to C:

const bool ENABLED_FLAG = true;
const int8 OFFSET = -33;
const uint16 ANSWER = 42;
const uint16 ANSWER_IN_BINARY = 0b101010;
const uint32 POPULATION_USA_2018 = 330000000;
const uint64 DIAMOND = 0x183c7effff7e3c18;
const uint64 FUCHSIA = 4054509061583223046;
const string USERNAME = "squeenze";
const float32 MIN_TEMP = -273.15;
const float64 CONVERSION_FACTOR = 1.41421358;
const Beverage MY_DRINK = Beverage.WATER;

These declarations introduce a name within their scope. The constant's type must be either a primitive or an enum.

Constant expressions are either literals or the names of other constant expressions.

For greater clarity, there is no expression processing in FIDL; that is, you cannot declare a constant as having the value 6 + 5, for example.

Default Initialization

Primitive structure members may have initialization values specified in the declaration. For example:

struct Scene {
    uint32 background_rgb = 0xFF77FF; // fuchsia is the default background
    uint32 foreground_rgb; // there is no default foreground color

If the programmer does not supply a background color, the default value of 0xFF77FF will be used.

However, if the program does not supply a foreground color, there is no default. The foreground color must be supplied; otherwise it's a logic error on the programmer's part.

There is a subtlety about the semantics and what defaults mean:

  • If the target language can support defaults (Dart, C++)
    • then it MUST support defaults
  • If the target language cannot support defaults (C, Rust, Go)
    • then it MAY provide support that programmers can optionally invoke (e.g., a macro in C).

Declaration Separator

FIDL uses the semi-colon ';' to separate adjacent declarations within the file, much like C.


Libraries are named containers of FIDL declarations.

// library identifier separated by dots
library fuchsia.composition;

// "using" to import library "fuchsia.buffers"
using fuchsia.buffers;

// "using" to import library "fuchsia.geometry" and create a shortform called "geo"
using fuchsia.geometry as geo;

Libraries may declare that they use other libraries with a "using" declaration. This allows the library to refer to symbols defined in other libraries upon which they depend. Symbols imported this way may be accessed by:

  • qualifying them with the fully qualified library name (as in "fuchsia.geometry.Rect"),
  • specifying just the library name (as in "geometry.Rect"), or,
  • using a library alias (as in "geo.Rect").

In the source tree, each library consists of a directory with some number of .fidl files. The name of the directory is irrelevant to the FIDL compiler but by convention it should resemble the library name itself. A directory should not contain FIDL files for more than one library.

The scope of "library" and "using" declarations is limited to a single file. Each individual file within a FIDL library must restate the "library" declaration together with any "using" declarations needed by that file.

The library's name may be used by certain language bindings to provide scoping for symbols emitted by the code generator.

For example, the C++ bindings generator places declarations for the FIDL library "fuchsia.ui" within the C++ namespace "fuchsia::ui". Similarly, for languages such as Dart and Rust, which have their own module system, each FIDL library is compiled as a module for that language.

Types and Type Declarations

FIDL supports a number of builtin types as well as declarations of new types (e.g. structs, unions, type aliases) and protocols.


  • Simple value types.
  • Not nullable.

The following primitive types are supported:

  • Boolean bool
  • Signed integer int8 int16 int32 int64
  • Unsigned integer uint8 uint16 uint32 uint64
  • IEEE 754 Floating-point float32 float64

Numbers are suffixed with their size in bits, bool is 1 byte.

We also alias byte to mean uint8 as a built-in alias.


// A record which contains fields of a few primitive types.
struct Sprite {
    float32 x;
    float32 y;
    uint32 index;
    uint32 color;
    bool visible;


  • Named bit types.
  • Discrete subset of bit values chosen from an underlying integer primitive type.
  • Not nullable.
  • Bits must have at least one member.
  • Bits can either be strict or flexible.
    • Bits default to strict.


strict bits InfoFeatures : uint8 {
    /// If present, this device represents WLAN hardware
    WLAN = 0x01;
    /// If present, this device is synthetic (not backed by h/w)
    SYNTH = 0x02;
    /// If present, this device receives all messages it sends
    LOOPBACK = 0x04;

// Underlying type is assumed to be uint32.
flexible bits AllowableSegments {
    TOLL_ROADS = 0b001;
    HIGHWAYS = 0b010;
    BIKE_PATHS = 0b100;


  • Proper enumerated types.
  • Discrete subset of named values chosen from an underlying integer primitive type.
  • Not nullable.
  • Enums must have at least one member.
  • Enums can be strict or flexible.
    • Enums default to strict.


The ordinal index is required for each enum element. The underlying type of an enum must be one of: int8, uint8, int16, uint16, int32, uint32, int64, uint64. If omitted, the underlying type is assumed to be uint32.

flexible enum Beverage : uint8 {
    WATER = 0;
    COFFEE = 1;
    TEA = 2;
    WHISKEY = 3;

// Underlying type is assumed to be uint32.
strict enum Vessel {
    CUP = 0;
    BOWL = 1;
    TUREEN = 2;
    JUG = 3;


Enum types are denoted by their identifier, which may be qualified if needed.

// A record which contains two enum fields.
struct Order {
    Beverage beverage;
    Vessel vessel;


  • Fixed-length sequences of homogeneous elements.
  • Elements can be of any type including: primitives, enums, arrays, strings, vectors, handles, structs, tables, unions.
  • Not nullable themselves; may contain nullable types.


Arrays are denoted array<T>:n where T can be any FIDL type (including an array) and n is a positive integer constant expression that specifies the number of elements in the array.

// A record which contains some arrays.
struct Arrays {
    // array of exactly 16 floating point numbers
    array<float32>:16 matrix;

    // array of exactly 10 arrays of 4 strings each
    array<array<string>:4>:10 form;


  • Variable-length sequence of UTF-8 encoded characters representing text.
  • Nullable; null strings and empty strings are distinct.
  • Can specify a maximum size, eg. string:40 for a maximum 40 byte string.
  • May contain embedded NUL bytes, unlike traditional C strings.


Strings are denoted as follows:

  • string : non-nullable string (validation error occurs if null is encountered)
  • string? : nullable string
  • string:N, string:N? : string, and nullable string, respectively, with maximum length of N bytes
// A record which contains some strings.
struct Document {
    // title string, maximum of 40 bytes long
    string:40 title;

    // description string, may be null, no upper bound on size
    string? description;

Strings should not be used to pass arbitrary binary data since bindings enforce valid UTF-8. Instead, consider bytes for small data or fuchsia.mem.Buffer for blobs. See Should I use string or vector? for details.


  • Variable-length sequence of homogeneous elements.
  • Nullable; null vectors and empty vectors are distinct.
  • Can specify a maximum size, eg. vector<T>:40 for a maximum 40 element vector.
  • There is no special case for vectors of bools. Each bool element takes one byte as usual.
  • We have a built-in alias for bytes to mean vector<uint8>, and it can be size bound in a similar fashion e.g. bytes:1024.


Vectors are denoted as follows:

  • vector<T> : non-nullable vector of element type T (validation error occurs if null is encountered)
  • vector<T>? : nullable vector of element type T
  • vector<T>:N, vector<T>:N? : vector, and nullable vector, respectively, with maximum length of N elements

T can be any FIDL type.

// A record which contains some vectors.
struct Vectors {
    // a vector of up to 10 integers
    vector<int32>:10 params;

    // a vector of bytes, no upper bound on size
    bytes blob;

    // a nullable vector of up to 24 strings
    vector<string>:24? nullable_vector_of_strings;

    // a vector of nullable strings, no upper bound on size
    vector<string?> vector_of_nullable_strings;

    // a vector of vectors of 16-element arrays of floating point numbers
    vector<vector<array<float32>:16>> complex;


  • Transfers a Zircon capability by handle value.
  • Stored as a 32-bit unsigned integer.
  • Nullable by encoding as a zero-valued handle.


Handles are denoted:

  • handle : non-nullable Zircon handle of unspecified type
  • handle? : nullable Zircon handle of unspecified type
  • zx.handle:H : non-nullable Zircon handle of type H
  • zx.handle:H? : nullable Zircon handle of type H

H can be any object supported by Zircon, e.g. channel, thread, vmo. Please refer to the grammar for a full list.

Structs, tables, and unions containing handles must be marked with the resource modifier.

// A record which contains some handles.
resource struct Handles {
    // a handle of unspecified type
    zx.handle h;

    // an optional channel
    zx.handle:CHANNEL? c;


  • Record type consisting of a sequence of typed fields.
  • Declaration is not intended to be modified once deployed; use protocol extension instead.
  • Declaration can have the resource modifier.
  • Reference may be nullable.
  • Structs contain zero or more members.


struct CirclePoint {
    float32 x;
    float32 y;

struct Color {
    float32 r;
    float32 g;
    float32 b;


Structs are denoted by their declared name (eg. Circle) and nullability:

  • Circle : non-nullable Circle
  • Circle? : nullable Circle
struct Circle {
    bool filled;
    CirclePoint center; // CirclePoint will be stored in-line
    float32 radius;
    Color? color; // Color will be stored out-of-line
    bool dashed;


  • Record type consisting of a sequence of typed fields with ordinals.
  • Declaration is intended for forward and backward compatibility in the face of schema changes.
  • Declaration can have the resource modifier.
  • Tables cannot be nullable. The semantics of "missing value" is expressed by an empty table i.e. where all members are absent, to avoid dealing with double nullability.
  • Tables contain zero or more members.


table Profile {
    1: vector<string> locales;
    2: vector<string> calendars;
    3: vector<string> time_zones;


Tables are denoted by their declared name (eg. Profile):

  • Profile : non-nullable Profile

Here, we show how Profile evolves to also carry temperature units. A client aware of the previous definition of Profile (without temperature units) can still send its profile to a server that has been updated to handle the larger set of fields.

enum TemperatureUnit {
    CELSIUS = 1;

table Profile {
    1: vector<string> locales;
    2: vector<string> calendars;
    3: vector<string> time_zones;
    4: TemperatureUnit temperature_unit;


  • Record type consisting of an ordinal and an envelope.
  • Ordinal indicates member selection, envelope holds contents.
  • Declaration can be modified after deployment, while maintaining ABI compatibility. See the Compatibility Guide for source-compatibility considerations.
  • Declaration can have the resource modifier.
  • Reference may be nullable.
  • Unions contain one or more members. A union with no members would have no inhabitants and thus would make little sense in a wire format.
  • Unions can either be strict or flexible.
    • Unions default to strict.


/// The result of an operation. A result is either a single number or an
/// [Error] value.
union Result {
    1: float64 number;
    2: reserved;
    3: Error error;


Unions are denoted by their declared name (e.g. Result) and nullability:

  • Either : non-nullable Result
  • Either? : nullable Result
union Either {
    1: Left left;
    2: Right right;

Strict vs. Flexible

FIDL declarations can either have strict or flexible behavior:

  • Bits, enums, and unions are strict unless declared with the flexible modifier.
  • Structs always have strict behavior.
  • Tables always have flexible behavior.

For strict types only, serializing or deserializing a value that contains data not described in the declaration is a validation error.

In this example:

flexible union FlexibleEither {
    1: Left left;
    2: Right right;

By virtue of being flexible, it is simpler for FlexibleEither to evolve to carry a third variant. A client aware of the previous definition of FlexibleEither without the third variant can still receive a union from a server that has been updated to contain the larger set of variants. If the union is of the unknown variant, bindings may expose it as unknown data (i.e. as raw bytes and handles) to the user and allow re-encoding the unknown union (e.g. to support proxy-like use cases). The methods provided for interacting with unknown data for flexible types are described in detail in the bindings reference.

More details are discussed in [RFC-0033: Handling of Unknown Fields and Strictness][rfc-033].

Value vs. Resource

Every FIDL type is either a value type or a resource type. Resource types include:

  • handles
  • protocol endpoints
  • aliases of resource types
  • arrays and vectors of resource types
  • structs, tables, and unions marked with the resource modifier
  • nullable references to any of the above types

All other types are value types.

Value types must not contain resource types. For example, this is incorrect:

struct Foo { // ERROR: must be "resource struct Foo"
    handle h;

Types can be marked with the resource modifier even if they do not contain handles. You should do this if you intend to add handles to the type in the future, since adding or removing the resource modifier requires source-compatibility considerations. For example:

// No handles now, but we will add some in the future.
resource table Record {
    1: string str;

// "Foo" must be a resource because it contains "Record", which is a resource.
resource struct Foo {
    Record record;

More details are discussed in RFC-0057: Default No Handles.


  • Describe methods that can be invoked by sending messages over a channel.
  • Methods are identified by their ordinal index. The compiler calculates the ordinal by
    • Taking the SHA-256 hash of the string generated by concatenating:
      • The UTF-8 encoded library name, with no trailing \0 character
      • '.' (ASCII 0x2e)
      • The UTF-8 encoded protocol name, with no trailing \0 character
      • '/' (ASCII 0x2f)
      • The UTF-8 encoded method name, with no trailing \0 character
    • Extracting the upper 32 bits of the hash value, and
    • Setting the upper bit of that value to 0.
    • To coerce the compiler into generating a different value, methods can have a Selector attribute. The value of the Selector attribute will be used in the place of the method name above.
  • Each method declaration states its arguments and results.

    • If no results are declared, then the method is one-way: no response will be generated by the server.
    • If results are declared (even if empty), then the method is two-way: each invocation of the method generates a response from the server.
    • If only results are declared, the method is referred to as an event. It then defines an unsolicited message from the server.
    • Two-way methods may declare an error type that a server can send instead of the response. This type must be an int32, uint32, or an enum thereof.
  • When a server of a protocol is about to close its side of the channel, it may elect to send an epitaph message to the client to indicate the disposition of the connection. The epitaph must be the last message delivered through the channel. An epitaph message includes a 32-bit int value of type zx_status_t. Negative values are reserved for system error codes. Positive values are reserved for application errors. A status of ZX_OK indicates successful operation.


enum DivisionError : uint32 {

protocol Calculator {
    Add(int32 a, int32 b) -> (int32 sum);
    Divide(int32 dividend, int32 divisor)
        -> (int32 quotient, int32 remainder) error DivisionError;
    -> OnError(uint32 status_code);


Protocols are denoted by their name, directionality of the channel, and optionality:

  • Protocol : non-nullable FIDL protocol (client endpoint of channel)
  • Protocol? : nullable FIDL protocol (client endpoint of channel)
  • request<Protocol> : non-nullable FIDL protocol request (server endpoint of channel)
  • request<Protocol>? : nullable FIDL protocol request (server endpoint of channel)
// A record which contains protocol-bound channels.
resource struct Record {
    // client endpoint of a channel bound to the Calculator protocol
    Calculator c;

    // server endpoint of a channel bound to the Science protocol
    request<Science> s;

    // optional client endpoint of a channel bound to the
    // RealCalculator protocol
    RealCalculator? r;

Protocol Composition

A protocol can include methods from other protocols. This is called composition: you compose one protocol from other protocols.

Composition is used in the following cases:

  1. you have multiple protocols that all share some common behavior(s)
  2. you have varying levels of functionality you want to expose to different audiences

Common behavior

In the first case, there might be behavior that's shared across multiple protocols. For example, in a graphics system, several different protocols might all share a common need to set a background and foreground color. Rather than have each protocol define their own color setting methods, a common protocol can be defined:

protocol SceneryController {
    SetBackground(Color color);
    SetForeground(Color color);

It can then be shared by other protocols:

protocol Drawer {
    compose SceneryController;
    Circle(int32 x, int32 y, int32 radius);
    Square(int32 x, int32 y, int32 diagonal);

protocol Writer {
    compose SceneryController;
    Text(int32 x, int32 y, string message);

In the above, there are three protocols, SceneryController, Drawer, and Writer. Drawer is used to draw graphical objects, like circles and squares at given locations with given sizes. It composes the methods SetBackground() and SetForeground() from the SceneryController protocol because it includes the SceneryController protocol (by way of the compose keyword).

The Writer protocol, used to write text on the display, includes the SceneryController protocol in the same way.

Now both Drawer and Writer include SetBackground() and SetForeground().

This offers several advantages over having Drawer and Writer specify their own color setting methods:

  • the way to set background and foreground colors is the same, whether it's used to draw a circle, square, or put text on the display.
  • new methods can be added to Drawer and Writer without having to change their definitions, simply by adding them to the SceneryController protocol.

The last point is particularly important, because it allows us to add functionality to existing protocols. For example, we might introduce an alpha-blending (or "transparency") feature to our graphics system. By extending the SceneryController protocol to deal with it, perhaps like so:

protocol SceneryController {
    SetBackground(Color color);
    SetForeground(Color color);
    SetAlphaChannel(int a);

we've now extended both Drawer and Writer to be able to support alpha blending.

Multiple compositions

Composition is not a one-to-one relationship — we can include multiple compositions into a given protocol, and not all protocols need be composed of the same mix of included protocols.

For example, we might have the ability to set font characteristics. Fonts don't make sense for our Drawer protocol, but they do make sense for our Writer protocol, and perhaps other protocols.

So, we define our FontController protocol:

protocol FontController {
    SetPointSize(int32 points);
    SetFontName(string fontname);
    Italic(bool onoff);
    Bold(bool onoff);
    Underscore(bool onoff);
    Strikethrough(bool onoff);

and then invite Writer to include it, by using the compose keyword:

protocol Writer {
    compose SceneryController;
    compose FontController;
    Text(int x, int y, string message);

Here, we've extended the Writer protocol with the FontController protocol's methods, without disturbing the Drawer protocol (which doesn't need to know anything about fonts).

Protocol composition is similar to mixin. More details are discussed in RFC-0023: Compositional Model.


At the beginning of this section, we mentioned a second use for composition, namely exposing various levels of functionality to different audiences.

In this example, we have two protocols that are independently useful, a Clock protocol to get the current time and timezone:

protocol Clock {
    Now() -> (Time time);
    CurrentTimeZone() -> (string timezone);

And an Horologist protocol that sets the time and timezone:

protocol Horologist {
    SetTime(Time time);
    SetCurrentTimeZone(string timezone);

We may not necessarily wish to expose the more privileged Horologist protocol to just any client, but we do want to expose it to the system clock component. So, we create a protocol (SystemClock) that composes both:

protocol SystemClock {
    compose Clock;
    compose Horologist;


Type aliasing is supported. For example:

alias StoryID = string:MAX;
alias Chapters = vector<StoryID>:5;

In the above, the identifier StoryID is an alias for the declaration of a string with a maximum size of MAX_SIZE. The identifier Chapters is an alias for a vector declaration of five StoryId elements.

The identifiers StoryID and Chapters can be used wherever their aliased definitions can be used. Consider:

struct Message {
    StoryID baseline;
    Chapters chapters;

Here, the Message struct contains a string of MAX_SIZE bytes called baseline, and a vector of up to 5 strings of MAX_SIZE called chapters.

Note that byte and bytes are built in aliases, see below.


FIDL provides several built-ins:

  • convenience types (byte and bytes)
  • zx library see below

Built-in aliases

The types byte and bytes are built-in, and are conceptually equivalent to:

library builtin;

alias byte = uint8;
alias bytes = vector<byte>;

When you refer to a name without specific scope, e.g.:

struct RawBytes {
    byte head;
    bytes rest;

we treat this as builtin.byte automatically (so long as there isn't a more-specific name in scope).

ZX Library

The fidlc compiler automatically generates an internal ZX library for you that contains commonly used Zircon definitions.