Google celebrates Hispanic Heritage Month. See how.

FIDL compiler error catalog

This document lists all errors emitted by the FIDL compiler, fidlc. Error identifiers in this domain are always rendered with the prefix fi- followed by a four digit code, like fi-0123.

fi-0018: Ordinals must start at 1

Neither table nor union member ordinal values are allowed to be 0:

library test.bad.cannotstartatzero;

type Foo = strict union {
    0: foo uint32;
    1: bar uint64;
};

Instead, numbering should start from 1:

library test.good.strictunion;

type Foo = strict union {
    1: foo uint32;
    2: bar uint64;
};

fi-0033: Conflicting modifiers

Certain modifiers are mutually exclusive of one another and cannot both modify the same declaration:

library test.bad.conflictingmodifiers;

type StrictFlexibleFoo = strict flexible union {
    1: b bool;
};

type FlexibleStrictBar = flexible strict union {
    1: b bool;
};

Only one of the strict or flexible modifiers may be used on a single declaration at a time:

library test.good.simplestrictness;

type FlexibleFoo = flexible union {
    1: i int32;
};

type StrictBar = strict union {
    1: i int32;
};

At this time, only the strict and flexible modifiers are mutually exclusive in this manner. The resource modifier has no reciprocal modifier, and thus has no such restrictions applied to it.

fi-0040: Files disagree on library name

Libraries can be composed of multiple files, but each file must have the same name:

library test.bad.filesdisagreeonlibraryname1;
library test.bad.filesdisagreeonlibraryname2;

Ensure that all files used by a library share the same name:

library test.good.librarymultiplefiles;
library test.good.librarymultiplefiles;

An encouraged convention for multi-file libraries is to create an otherwise empty overview.fidl file to serve as the main "entry point" into the library. The overview.fidl file is also the appropriate place to put library-scoped @available platform specifications.

fi-0057: Includes cycle

There are a number of situations that could cause this problem to occur, but all of them basically boil down to a FIDL declaration referring to itself in an unresolvable way. The simplest form of this error is when a type or protocol refers directly to itself in its own definition:

library test.bad.selfreferentialstruct;

type MySelf = struct {
    me MySelf;
};

More complex failure cases are possible when a type or protocol transitively refers to itself via at least one level of indirection:

library test.bad.recursivestructreference;

type Yin = struct {
    yang Yang;
};

type Yang = struct {
    yin Yin;
};
library test.bad.recursiveprotocolreference;

protocol Yin {
    compose Yang;
};

protocol Yang {
    compose Yin;
};

This error can be resolved by adding an envelope (aka, optionality) somewhere in the inclusion cycle, as this allows the cycle to be "broken" at encode/decode time:

library test.good.recursivetype;

type MySelf = struct {
    me box<MySelf>;
};
library test.bad.selfreferentialtable;

type MySelf = table {
    1: me MySelf;
};

Recursive types that are unbroken by envelopes are disallowed because they would be impossible to encode. In the first example above, encoding MySelf would require first encoding an instance of MySelf, which would in turn require encoding an instance of MySelf, ad inifitum. A solution to this problem is to add a "break" in this chain via optionality, where one may choose to either encode another nested instance of MySelf, or otherwise encode a null envelope with no further data.

fi-0066: Constant overflows type

A constant value cannot fall outside of the range inherent to its underlying type:

library test.bad.uintoverflow;

const NUM uint64 = -42;

The problem may be fixed either by changing the value to fit within the type's range:

library test.good.simpleuintconst;

const NUM uint64 = 42;

Or otherwise by changing to the type to accommodate the currently overflowing value:

library test.good.simpleintconst;

const NUM int64 = -42;

This error exclusively concerns FIDL's numeric types, all of which have the capacity to overflow. The ranges are sourced from the C++ std::numeric_limits interface and are as follows:

Type Minimum Maximum
int8 -128 127
int16 32768 32767
int32 2147483648 2147483647
int64 9223372036854775808 9223372036854775807
uint8 0 255
uint16 0 65536
uint32 0 4294967295
uint64 0 18446744073709551615
float32 -3.40282e+38 3.40282e+38
float64 -1.79769e+308 1.79769e+308

fi-0067: Bits member must be power of two

The values of all members in a bits declaration must not be any number that is not a power of two:

library test.bad.bitsnonpoweroftwo;

type NonPowerOfTwo = bits : uint64 {
    THREE = 3;
};

Instead, member values should always be powers of two:

library test.good.simplebits;

type Fruit = bits : uint64 {
    ORANGE = 1;
    APPLE = 2;
    BANANA = 4;
};

An easy way to avoid getting tripped up by this restriction is to just use bit masks, rather than decimal numbers, for bit member values:

library test.good.maskedbits;

type Life = bits {
    A = 0b000010;
    B = 0b001000;
    C = 0b100000;
};

The bits construct represents a bit array. This is the most memory-efficient way to represent a sequence of boolean flags. Because each member of the bits declaration is mapped to a specific bit of its underlying memory, the values used for that mapping must clearly identify a specific bit in an unsigned integer to assign to.

fi-0093: Max ordinal in table must be table

The type of the 64th member in a FIDL table must itself be a table:

library test.bad.maxordinalnottable;

type Example = table {
    1: v1 int64;
    2: v2 int64;
    3: v3 int64;
    4: v4 int64;
    5: v5 int64;
    6: v6 int64;
    7: v7 int64;
    8: v8 int64;
    9: v9 int64;
   10: v10 int64;
   11: v11 int64;
   12: v12 int64;
   13: v13 int64;
   14: v14 int64;
   15: v15 int64;
   16: v16 int64;
   17: v17 int64;
   18: v18 int64;
   19: v19 int64;
   20: v20 int64;
   21: v21 int64;
   22: v22 int64;
   23: v23 int64;
   24: v24 int64;
   25: v25 int64;
   26: v26 int64;
   27: v27 int64;
   28: v28 int64;
   29: v29 int64;
   30: v30 int64;
   31: v31 int64;
   32: v32 int64;
   33: v33 int64;
   34: v34 int64;
   35: v35 int64;
   36: v36 int64;
   37: v37 int64;
   38: v38 int64;
   39: v39 int64;
   40: v40 int64;
   41: v41 int64;
   42: v42 int64;
   43: v43 int64;
   44: v44 int64;
   45: v45 int64;
   46: v46 int64;
   47: v47 int64;
   48: v48 int64;
   49: v49 int64;
   50: v50 int64;
   51: v51 int64;
   52: v52 int64;
   53: v53 int64;
   54: v54 int64;
   55: v55 int64;
   56: v56 int64;
   57: v57 int64;
   58: v58 int64;
   59: v59 int64;
   60: v60 int64;
   61: v61 int64;
   62: v62 int64;
   63: v63 int64;
   64: v64 int64;
};

Users that find themselves needing to add a 64th member should create a separate table to hold members 64 and beyond, and place that member in the table instead:

library test.good.maxordinaltable;

type Table64thField = table {
    1: x int64;
};

type Example = table {
    1: v1 int64;
    2: v2 int64;
    3: v3 int64;
    4: v4 int64;
    5: v5 int64;
    6: v6 int64;
    7: v7 int64;
    8: v8 int64;
    9: v9 int64;
   10: v10 int64;
   11: v11 int64;
   12: v12 int64;
   13: v13 int64;
   14: v14 int64;
   15: v15 int64;
   16: v16 int64;
   17: v17 int64;
   18: v18 int64;
   19: v19 int64;
   20: v20 int64;
   21: v21 int64;
   22: v22 int64;
   23: v23 int64;
   24: v24 int64;
   25: v25 int64;
   26: v26 int64;
   27: v27 int64;
   28: v28 int64;
   29: v29 int64;
   30: v30 int64;
   31: v31 int64;
   32: v32 int64;
   33: v33 int64;
   34: v34 int64;
   35: v35 int64;
   36: v36 int64;
   37: v37 int64;
   38: v38 int64;
   39: v39 int64;
   40: v40 int64;
   41: v41 int64;
   42: v42 int64;
   43: v43 int64;
   44: v44 int64;
   45: v45 int64;
   46: v46 int64;
   47: v47 int64;
   48: v48 int64;
   49: v49 int64;
   50: v50 int64;
   51: v51 int64;
   52: v52 int64;
   53: v53 int64;
   54: v54 int64;
   55: v55 int64;
   56: v56 int64;
   57: v57 int64;
   58: v58 int64;
   59: v59 int64;
   60: v60 int64;
   61: v61 int64;
   62: v62 int64;
   63: v63 int64;
   64: v64 Table64thField;
};

The reasoning and motivations behind this requirement are fully elaborated in RFC-0132: FIDL table size limit. In short, FIDL tables need to have relatively constrained limits on the number of fields that they allow, otherwise the encoded form of a table that only uses a few fields at a time would have an unacceptably large amount of dead space (16 bytes for every omitted member).

To give users who want more than 64 fields in their tables a workaround, FIDL forces the last ordinal to be reserved for a "continuation table" that contains the extra fields. Using any other type in this position would render the table unextendable going forward.

fi-0110: Resource containing types must be marked resource

A type that includes a handle, either directly or through the transitive inclusion of another handle-containing type, cannot be declared without that type being specified as a resource:

library test.bad.missingresourceness;

using zx;

type Foo = struct {
    handle zx.handle;
};

There are two possible solutions. The first is to annotate the offending declaration with the resource modifier:

library test.good.simpleresourceness;

using zx;

type Foo = resource struct {
    handle zx.handle;
};

Alternatively, one could opt to remove the resource-including type completely, thereby obviating the need for the modifier on the owning declaration:

library test.good.simplestruct;

type Foo = struct {
    value uint64;
};

The reasoning and motivations behind the addition of the resource modifier and the "infectious" nature of the usage pattern enforced by this error can be found in RFC-0057: Default no handles.

fi-0158: Cannot bound twice

An alias declaration cannot change the value of an already set constraint on the type it is aliasing:

library test.bad.vectorboundtwice;

alias ByteVec256 = vector<uint8>:256;
alias ByteVec512 = ByteVec256:512;

Instead, the unbounded definition should receive its own alias declaration, and each further constrained alias should inherit from it in turn:

library test.good.unboundedvectorboundtwice;

alias AliasOfVectorOfString = vector<string>;
alias AliasOfVectorOfStringSmall = AliasOfVectorOfString:8;
alias AliasOfVectorOfStringLarge = AliasOfVectorOfString:16;

This is disallowed to avoid confusion and compiler implementation complexity.

fi-0167: Cannot constrain twice

Re-assigning the transport bound for a client_end or server_end that has already had a transport bound defined through an alias declaration is prohibited:

library test.bad.transportsreassigned;

protocol MyOtherProtocol {};

alias ClientEnd = client_end:MyProtocol;
alias ServerEnd = server_end:MyProtocol;

protocol MyProtocol {
    MyMethod(resource struct {
        c ClientEnd:MyOtherProtocol;
    }) -> (resource struct {
        s ServerEnd:MyOtherProtocol;
    });
};

Instead, aliasing of client_end and server_end types should be avoided entirely:

library test.good.simpletransports;

protocol MyProtocol {
    MyMethod(resource struct {
        c client_end:MyProtocol;
    }) -> (resource struct {
        s server_end:MyProtocol;
    });
};

This is disallowed to avoid confusion and compiler implementation complexity.

fi-0178: Unused import

Not referencing a dependency imported via the using declaration is an error:

library test.bad.unusedimport;

using dependent;

type Foo = struct {
    does_not int64;
    use_dependent int32;
};

Make sure all such imports are used in the library importing, either by actually referencing the import, or removing the unused dependency:

library test.good.simpleimport;

using dependent;

type Foo = struct {
    dep dependent.Bar;
};