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-0001: Invalid character
The lexer failed to convert a character into a token at the specified location.
library test.bad.fi0001;
type ßar = struct {
value uint64;
};
An invalid character should be fixed by substitution or removal.
library test.good.fi0001;
type Foo = struct {
value uint64;
};
Invalid characters are location dependent. Please refer to the FIDL language specification to determine which characters are permitted in each part of the FIDL syntax.
fi-0002: Unexpected line break
String literals are not allowed to be split across multiple lines:
library test.bad.fi0002;
const BAD_STRING string:1 = "Hello
World";
Instead, use the escape sequence \n
to represent a line break:
library test.good.fi0002;
const GOOD_STRING string:11 = "Hello\nWorld";
fi-0003: Invalid escape sequence
The lexer encountered an invalid character at the beginning of an escape sequence.
library test.bad.fi0003;
const UNESCAPED_BACKSLASH string:2 = "\ ";
const BACKSLASH_TYPO string:1 = "\i";
const CODE_POINT_TYPO string:1 = "\Y1F604";
Substitute a valid character to begin the escape sequence, or remove the unintended backslash character.
library test.good.fi0003;
const ESCAPED_BACKSLASH string:2 = "\\ ";
const REMOVED_BACKSLASH string:1 = "i";
const SMALL_CODE_POINT string:3 = "\u{2604}";
const BIG_CODE_POINT string:4 = "\u{01F604}";
Refer to the FIDL grammar specification for valid escape sequences.
fi-0004: Invalid hex digit
Unicode escapes in string literals must not contain invalid hex digits:
library test.bad.fi0004;
const SMILE string = "\u{1G600}";
You must specify a valid Unicode code point in hexadecimal, from 0 to 10FFFF.
Each hex digit must be a number from 0 to 9, a lowercase letter from a
to f
,
or an uppercase letter from A
to F
. In this case, G
was a typo for F
:
library test.good.fi0004;
const SMILE string = "\u{1F600}";
fi-0005
fi-0006: Expected declaration
This error happens when FIDL expects a declaration and finds something else.
This is often caused by a typo. The valid declarations are: type
, alias
,
const
, using
, protocol
, and service
.
library test.bad.fi0006;
cosnt SPELLED_CONST_WRONG string:2 = ":("; // Expected a declaraction (such as const).
To fix this error, check for typos in your top-level declarations and ensure you're only using features that are supported by FIDL.
library test.good.fi0006;
const SPELLED_CONST_RIGHT string:2 = ":)";
fi-0007: Unexpected token
This error occurs when an unexpected token is encountered during parsing. Generally speaking, this is the result of a typo:
library test.bad.fi0007;
alias MyType = vector<uint8>:<,256,optional>; // Extra leading comma
The fix to this problem is typically to remove the unexpected token, or in some cases, provide the rest of the missing syntax:
library test.good.fi0007;
alias MyType = vector<uint8>:<256, optional>;
fi-0008: Unexpected token
This error happens whenever the FIDL parser encounters a grammatically invalid
token. This can happen many ways--for example a missing =
for an enum member,
an extra token such as library = what.is.that.equals.doing.there
, and so on.
library test.bad.unexpectedtokenofkind;
type Numbers = flexible enum {
ONE; // FIDL enums don't have a default value.
};
Generally, the fix to this problem will be adding a missing token or removing an extra one.
library test.good.fi0008;
type Numbers = flexible enum {
ONE = 1;
};
To avoid this error, do a once over on your *.fidl
files to make sure they're
grammatically correct.
fi-0009: Unexpected identifier
This error usually occurs when an identifier is used in an incorrect position:
using test.bad.fi0009;
Use the correct identifier instead:
library test.good.fi0009;
fi-0010: Invalid identifier
An identifier was found which does not meet the requirements for valid
identifiers. FIDL identifiers may contain alphanumerics and underscores
(specifically A-Z
, a-z
, 0-9
, and _
), and additionally each identifier
must begin with a letter and end with either a letter or number.
library test.bad.fi0010a;
// Foo_ is not a valid identifier because it ends with '_'.
type Foo_ = struct {
value uint64;
};
To fix this, change the identifier to make sure it contains only valid characters, starts with a letter, and ends with a letter or number.
library test.good.fi0010a;
type Foo = struct {
value uint64;
};
This error may also occur if a multi-part (dotted) identifier is passed to an attribute.
library test.bad.fi0010b;
@foo(bar.baz="Bar", zork="Zoom")
type Empty = struct{};
To fix this, change to use only single-part identifiers in attributes.
library test.good.fi0010b;
@foo(bar="Bar", zork="Zoom")
type Empty = struct {};
fi-0011: Invalid library name component
Library names must only contain letters and numbers (A-Z
, a-z
, and 0-9
),
and must start with a letter.
library test.bad.fi0011.name_with_underscores;
To fix this, ensure that all library name components meet the requirements.
library test.good.fi0011.namewithoutunderscores;
fi-0012: Invalid type layout class
Type declarations must specify a layout known to FIDL:
library test.bad.fi00012;
type Foo = invalid {};
The valid layouts are bits
, enum
, struct
, table
, and union
:
library test.good.fi0012;
type Empty = struct {};
A layout is a parameterizable description of a FIDL
type. It refers to a family of would-be types which can receive further
arguments to specify their shape. For example, a struct
is a kind of layout
which becomes a concrete type when it has specific members defined, while an
array
is a layout that becomes concrete when given a type to be repeated
sequentially a specified number of times.
Layouts are all built into the FIDL language - there is no means by which users can specify their own layouts, or create their own generic type templates.
fi-0013: Invalid wrapped type
This error occurs when the value passed to an enum or bits declaration isn't an identifier for a type, such as when you instead provide a string value as the "backing type":
library test.bad.fi0013;
type TypeDecl = enum : "int32" {
FOO = 1;
BAR = 2;
};
To fix this error, make sure the backing type for the enum or bits is a type identifier.
library test.good.fi0013;
type TypeDecl = enum : int32 {
FOO = 1;
BAR = 2;
};
fi-0014: Attribute with empty parentheses
This error occurs if an attribute has parentheses but no arguments.
library test.bad.fi0014;
@discoverable()
protocol MyProtocol {};
To fix it, remove the parentheses from attributes with no arguments, or supply arguments if arguments were intended.
library test.good.fi0014;
@discoverable
protocol MyProtocol {};
FIDL disallows empty argument lists on attributes mostly as a stylistic choice.
fi-0015: Attribute args must all be named
For clarity, when an attribute has more than one argument, all of the attribute's arguments must be explicitly named.
This error occurs when an attribute has more than one argument but does not explicitly provide names for the arguments.
library test.bad.fi0015;
@foo("abc", "def")
type MyStruct = struct {};
To fix it, provides names for all of the arguments using name=value
syntax.
library test.good.fi0015;
@foo(bar="abc", baz="def")
type MyStruct = struct {};
fi-0016: Missing ordinal before member
This error occurs when a fields in a union or table is missing an ordinal.
library test.bad.fi0016a;
type Foo = table {
x int64;
};
library test.bad.fi0016b;
type Bar = union {
foo int64;
bar vector<uint32>:10;
};
To fix this error, explicitly specify the ordinals for the table or union:
library test.good.fi0016;
type Foo = table {
1: x int64;
};
type Bar = union {
1: foo int64;
2: bar vector<uint32>:10;
};
Unlike structs, tables and unions are designed to allow backwards-compatible changes to be made to their contents. In order to enable this, a consistent value, the ordinal, is needed to identify table fields or union variants. To avoid confusion and make it harder to accidentally change ordinals when changing a table or union, ordinals must always be specified explicitly.
fi-0017: Ordinal out of bound
Ordinals for tables and Unions must be valid unsigned 32 bit integers. Negative ordinals or ordinals greater than 4,294,967,295 will cause this error.
library test.bad.fi0017a;
type Foo = table {
-1: foo string;
};
library test.bad.fi0017b;
type Bar = union {
-1: foo string;
};
To fix this error, ensure that all ordinals are in the allowed range.
library test.good.fi0017;
type Foo = table {
1: foo string;
};
type Bar = union {
1: foo string;
};
fi-0018: Ordinals must start at 1
Neither table
nor union
member ordinal values are allowed to be 0:
library test.bad.fi0018;
type Foo = strict union {
0: foo uint32;
1: bar uint64;
};
Instead, numbering should start from 1:
library test.good.fi0018;
type Foo = strict union {
1: foo uint32;
2: bar uint64;
};
fi-0019: Strict bits, enum, or union cannot be empty
A strict bits, enum, or union is not allowed to have zero members:
library test.bad.fi0019;
type Numbers = strict enum {};
Instead, there must be at least one member:
library test.good.fi0019a;
type Numbers = flexible enum {};
Alternatively, you can mark the declaration flexible
instead of strict
:
library test.good.fi0019b;
type Numbers = strict enum {
ONE = 1;
};
An empty bits, enum, or union carries no information, so it should not normally
be used in an API. However, flexible data types are designed for evolution, so
it makes sense to define a flexible bits or enum that starts out empty, with the
expectation of adding members later on. You should always think carefully about
whether to use strict
or flexible
when defining a new data type.
fi-0020: Invalid protocol member
This error occurs when an item in a protocol is not recognized as a valid protocol member such as when something in a protocol is not a protocol composition, one-way method, two-way method, or event.
library test.bad.fi0020;
protocol Example {
NotAMethodOrCompose;
};
To fix this error, remove the invalid items or convert them to the correct syntax for the type of protocol item they were intended to be.
library test.good.fi0020;
protocol Example {
AMethod();
};
fi-0021
fi-0022: Cannot attach attribute to identifier
This error occurs when an attribute is placed on the type of a declaration when that type is an identifier type. For example, placing an attribute after field name but before the field's type in a struct declaration associates the attribute with the type of the field rather than with the field itself. If the type of the field is a preexisting type being referenced by name, additional attributes cannot be applied to it.
library test.bad.fi0022;
type Foo = struct {
// uint32 is an existing type, extra attributes cannot be added to it just
// for this field.
data @foo uint32;
};
If the intent was to apply an attribute to the field, the attribute should be moved before the field name.
Attributes can be applied to types where they are declared. This means that if the type of a struct field or other similar declaration is an anonymous type rather than an identifier type, attributes can be applied to the type.
library test.good.fi0022;
type Foo = struct {
// The foo attribute is associated with the data1 field, not the uint32
// type.
@foo
data1 uint32;
// The type of data2 is a newly declared anonymous structure, so that new
// type can have an attribute applied to it.
data2 @foo struct {};
};
fi-0023: Attribute inside type declaration
With inline layouts, you can put attributes directly before the layout. However, when declaring a type at the top level, you cannot:
library test.bad.fi0023;
type Foo = @foo struct {};
Instead, you must put attributes before the type
keyword:
library test.good.fi0023;
@foo
type Foo = struct {};
We enforce this because it would be confusing to allow attributes in two places.
fi-0024: Doc comment on method parameter list
Method parameter lists cannot carry doc comments:
library test.bad.fi0024;
protocol Example {
Method(/// This is a one-way method.
struct {
b bool;
});
};
For the time being, place doc comments on the method itself:
library test.good.fi0024;
protocol Example {
/// This is a one-way method.
Method(struct {
b bool;
});
};
This error will no longer exist once this bug is resolved. The error a holdover from the migration to describe method payloads using FIDL types, rather than parameter lists.
fi-0025: Imports group must be at top of file
Except for the library
declaration at the top of the file, there cannot be any
other declarations before the using
imports for the file, should they exist:
library test.bad.fi0025;
alias i16 = int16;
using dependent;
type UsesDependent = struct {
field dependent.Something;
};
To resolve this error, place all of your using
imports in a block directly
after the library
declaration:
library test.good.fi0025;
using dependent;
alias i16 = int16;
type UsesDependent = struct {
field dependent.Something;
};
This rule reflects a primarily aesthetic decision by the FIDL team that dependencies are easier to read when they are well-grouped and easily located.
fi-0026: Comment inside doc comment block
Comments should not be placed inside of a doc comment block:
library test.bad.fi0026;
/// start
// middle
/// end
type Empty = struct {};
Instead, comments should be placed before or after the doc comment block:
library test.good.fi0026;
// some comments above,
// maybe about the doc comment
/// A
/// multiline
/// comment!
// another comment, maybe about the struct
type Empty = struct {};
Generally, comments immediately preceding the doc comment block are the best place for comments about the doc comment itself.
fi-0027: Blank lines in doc comment block
There should be no blank lines in a doc comment block:
library test.bad.fi0027;
/// start
/// end
type Empty = struct {};
Instead, blank lines should only be placed before or after the doc comment block:
library test.good.fi0027a;
/// A doc comment
type Empty = struct {};
Alternatively, consider omitting the blank lines altogether:
library test.good.fi0027b;
/// A doc comment
type Empty = struct {};
fi-0028: Doc comment must be followed by a declaration
Doc comments are never allowed to be free-floating, like regular comments:
library test.bad.fi0028;
type Empty = struct {};
/// bad
Doc comments must directly precede FIDL declarations in all circumstances:
library test.good.fi0028a;
/// A doc comment
type Empty = struct {};
FIDL "lowers" doc comments to @doc
attributes during compilation. In fact, any
comment can be written directly in such a manner if so desired:
library test.good.fi0028b;
@doc("An attribute doc comment")
type Empty = struct {};
Standalone doc comments are non-compilable from a technical perspective, but are also confusing semantically: what does it mean to "document" nothing? Unlike regular comments, doc comments get processed into structured documentation, and thus must be clear about which FIDL construct they are attached to.
fi-0029: Resource definition must have at least one property
Resource definitions with no properties specified are prohibited:
library test.bad.resourcedefinitionnoproperties;
resource_definition SomeResource : uint32 {
properties {};
};
Please specify at least one property:
library test.good.fi0029;
resource_definition SomeResource : uint32 {
properties {
subtype strict enum : uint32 {
NONE = 0;
};
};
};
This is an error related to FIDL's internal implementation, and thus should only ever be surfaced to developers working on FIDL's core libraries. End users should never see this error.
The resource_definition
declaration it refers to is FIDL's internal means of
defining resources like handles, and is likely to change in the future as part
of the handle generalization effort.
fi-0030: Invalid modifier
Each FIDL modifier has a specific set of declarations in which it can be used. Using the modifier in a prohibited declaration is not allowed:
library test.bad.fi0030;
type MyStruct = strict struct {
i int32;
};
The best course of action is to remove the offending modifier:
library test.good.fi0030;
type MyStruct = struct {
i int32;
};
fi-0031: Only bits and enum can have subtype
Not every FIDL layout can carry a subtype:
library test.bad.fi0031;
type Foo = flexible union : uint32 {};
Only bits
and enum
layouts are defined over an underlying type.
library test.good.fi0031;
type Foo = flexible enum : uint32 {};
The bits
and enum
layouts are somewhat unique, in that they are just
constrained subtypings of the integral FIDL primitives. Because of this, it
makes sense for them to specify an underlying type which acts as this subtype.
Conversely, struct
, table
, and union
layouts can be arbitrarily large and
can contain many members, therefore a global, layout-wide subtype does not make
sense.
fi-0032: Duplicate modifiers disallowed
Specifying the same modifier in a single declaration is prohibited:
library test.bad.fi0032;
type MyUnion = strict resource strict union {
1: foo bool;
};
Remove the duplicated modifier:
library test.good.fi0032;
type MyUnion = resource strict union {
1: foo bool;
};
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.fi0033;
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-0034: Name collision
Two declarations cannot have the same name:
library test.bad.fi0034;
const COLOR string = "red";
const COLOR string = "blue";
Instead, give each declaration a unique name:
library test.good.fi0034b;
const COLOR string = "red";
const OTHER_COLOR string = "blue";
Alternatively, remove one of the declarations if it was added by mistake:
library test.good.fi0034a;
const COLOR string = "red";
For more information on choosing names, see the FIDL style guide.
fi-0035: Canonical name collision
Two declarations cannot have the same canonical name:
library test.bad.fi0035;
const COLOR string = "red";
protocol Color {};
Even though COLOR
and Color
look different, they are both represented by the
canonical name color
. You get the canonical name by converting the
original name to snake_case
.
To fix the error, give each declaration a name that is unique after canonicalization:
library test.good.fi0035;
const COLOR string = "red";
protocol ColorMixer {};
Following the FIDL style guide's naming guidelines will minimize your chances of running into this error. Canonical name collisions will never happen between declarations that use the same casing style, and they will rarely happen between ones that use different styles because of other requirements (e.g. protocol names should usually be noun phrases ending in -er).
FIDL enforces this rule because bindings generators transform names to the idiomatic naming style for the target language. By ensuring unique canonical names, we guarantee that bindings can do this without producing name collisions. See RFC-0040: Identifier uniqueness for more details.
fi-0036: Name overlap
Declarations with the same name cannot have overlapping availabilities:
@available(added=1)
library test.bad.fi0036;
type Color = strict enum {
RED = 1;
};
@available(added=2)
type Color = flexible enum {
RED = 1;
};
Instead, use the @available
attribute to make sure only one of the
declarations is present at any given version:
@available(added=1)
library test.good.fi0036;
@available(replaced=2)
type Color = strict enum {
RED = 1;
};
@available(added=2)
type Color = flexible enum {
RED = 1;
};
Alternatively, rename or remove one of the declarations as shown in fi-0034.
See FIDL versioning to learn more about versioning.
fi-0037: Canonical name overlap
Declarations with the same canonical name cannot have overlapping availabilities:
@available(added=1)
library test.bad.fi0037;
const COLOR string = "red";
@available(added=2)
protocol Color {};
Even though COLOR
and Color
look different, they are both represented by the
canonical name color
. You get the canonical name by converting the
original name to snake_case
.
To fix the error, give each declaration a name that is unique after canonicalization.
@available(added=1)
library test.good.fi0037;
const COLOR string = "red";
@available(added=2)
protocol ColorMixer {};
Alternatively, change one of the declarations availabilities as shown in fi-0036, or remove the declaration.
See fi-0035 for more details on why FIDL requires declarations to have unique canonical names.
fi-0038: Name conflicts with import
A declaration cannot have the same name as a library imported with using
:
library dependency;
const VALUE uint32 = 1;
library test.bad.fi0038b;
using dependency;
type dependency = struct {};
// Without this, we'd get fi-0178 instead.
const USE_VALUE uint32 = dependency.VALUE;
Instead, import the library under a different name with the using
... as
syntax:
library test.good.fi0038b;
using dependency as dep;
type dependency = struct {};
const USE_VALUE uint32 = dep.VALUE;
Alternatively, rename the declaration to avoid the conflict:
library test.good.fi0038c;
using dependency;
type OtherName = struct {};
const USE_VALUE uint32 = dependency.VALUE;
You can avoid this problem by using multiple components in library names. For
example, FIDL libraries in the Fuchsia SDK start with fuchsia.
, so they have
at least two components and cannot conflict with declaration names.
This error exists to prevent ambiguities. For example, if dependency
were an
enum with a member called VALUE
, it would be ambiguous whether
dependency.VALUE
referred to that enum member or to the const declared in the
imported library.
fi-0039: Canonical name conflicts with import
A declaration cannot have the same canonical name as a library import with
using
:
library dependency;
const VALUE uint32 = 1;
library test.bad.fi0039b;
using dependency;
type Dependency = struct {};
// Without this, we'd get fi-0178 instead.
const USE_VALUE uint32 = dependency.VALUE;
Even though dependency
and Dependency
look different, they are both
represented by the canonical name dependency
. You get the canonical name
by converting the original name to snake_case
.
To fix the error, import the library under a different name with the using
...
as
syntax:
library test.good.fi0039b;
using dependency as dep;
type Dependency = struct {};
const USE_VALUE uint32 = dep.VALUE;
Alternatively, rename the declaration to avoid the conflict:
library test.good.fi0039c;
using dependency;
type OtherName = struct {};
const USE_VALUE uint32 = dependency.VALUE;
See fi-0038 to learn why this error exists and how to avoid it.
See fi-0035 for more details on why FIDL requires declarations to have unique canonical names.
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.fi0040a;
library test.bad.fi0040b;
Ensure that all files used by a library share the same name:
library test.good.fi0040;
library test.good.fi0040;
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-0041: Multiple libraries with same name
Each library passed to fidlc
must have a unique name:
library test.bad.fi0041;
library test.bad.fi0041;
Ensure that all libraries have unique names:
library test.good.fi0041a;
library test.good.fi0041b;
This error is often caused by arguments being supplied to fidlc
in an
incorrect manner. The constituent files that make up each library necessary for
compilation (that is, the library being compiled and all of its transitive
dependencies) must be supplied as a single space-separated list of files passed
via the --files
argument, with one such flag per library. A common mistake is
to try to pass the file for all libraries in a single --files
list.
fi-0042: Duplicate library import
Dependencies cannot be imported multiple times:
library test.bad.fi0042a;
type Bar = struct {};
library test.bad.fi0042b;
using test.bad.fi0042a;
using test.bad.fi0042a; // duplicated
type Foo = struct {
bar test.bad.fi0042a.Bar;
};
Ensure that each dependency is only imported once:
library test.good.fi0042a;
type Bar = struct {};
library test.good.fi0042b;
using test.good.fi0042a;
type Foo = struct {
bar test.good.fi0042a.Bar;
};
It is worth noting that FIDL does not support importing different versions of
the same library. The @available
version is resolved for the entire fidlc
compilation via the --available
flag, meaning that both the library being
compiled and all of its dependencies must share the same version for any given
compilation run.
fi-0043: Conflicting library imports
Imported libraries are prohibited from being aliased in such a way that they conflict with the non-aliased names of other imported libraries:
library test.bad.fi0043a;
type Bar = struct {};
// This library has a one component name to demonstrate the error.
library fi0043b;
type Baz = struct {};
library test.bad.fi0043c;
using test.bad.fi0043a as fi0043b; // conflict
using fi0043b;
type Foo = struct {
a fi0043b.Bar;
b fi0043b.Baz;
};
Choose a different alias to resolve the name conflict:
library test.good.fi0043a;
type Bar = struct {};
library fi0043b;
type Baz = struct {};
library test.good.fi0043c;
using test.good.fi0043a as dep;
using fi0043b;
type Foo = struct {
a dep.Bar;
b fi0043b.Baz;
};
fi-0044: Conflicting library import aliases
Imported libraries are prohibited from being aliased in such a way that they conflict with the aliases assigned to other imported libraries:
library test.bad.fi0044a;
type Bar = struct {};
library test.bad.fi0044b;
type Baz = struct {};
library test.bad.fi0044c;
using test.bad.fi0044a as dep;
using test.bad.fi0044b as dep; // conflict
type Foo = struct {
a dep.Bar;
b dep.Baz;
};
Choose non-conflicting aliases to resolve the name conflict:
library test.good.fi0044a;
type Bar = struct {};
library test.good.fi0044b;
type Baz = struct {};
library test.good.fi0044c;
using test.good.fi0044a as dep1;
using test.good.fi0044b as dep2;
type Foo = struct {
a dep1.Bar;
b dep2.Baz;
};
fi-0045: Attributes disallowed on using declarations
Attributes cannot be attached to using
declarations:
library test.bad.fi0045a;
type Bar = struct {};
library test.bad.fi0045b;
/// not allowed
@also_not_allowed
using test.bad.fi0045a;
type Foo = struct {
bar test.bad.fi0045a.Bar;
};
Remove the attribute to resolve the error:
library test.good.fi0045a;
type Bar = struct {};
library test.good.fi0045b;
using test.good.fi0045a;
type Foo = struct {
bar test.good.fi0045a.Bar;
};
This restriction applies to /// ...
doc comments as well, as those are merely
syntactic sugar for the @doc("...")
attribute.
fi-0046: Unknown library
In most cases, this problem is due to the dependency being misspelled or not provided by the build system. If the dependency in question is intentionally unused, the relevant using line must be removed:
library test.bad.fi0046;
using dependent; // unknown using.
type Foo = struct {
dep dependent.Bar;
};
Make sure all imports are added as dependencies to the library using the build system.
library test.good.fi0046;
type Foo = struct {
dep int64;
};
fi-0047
fi-0048: Optional table member
Table members types cannot be optional
:
library test.bad.fi0048;
type Foo = table {
// Strings can be optional in general, but not in table member position.
1: t string:optional;
};
Remove the optional
constraint from all members:
library test.good.fi0048;
type Foo = table {
1: t string;
};
Table members are always optional, so specifying this fact on the member's underlying type is redundant.
Table members are always optional because, on the wire, each table member is represented as an entry in a vector. This vector is always represents all known fields on the table, so every omitted table member is represented as a null envelope - exactly equivalent to the representation of an omitted optional type.
fi-0049: Optional union member
Union members cannot be optional:
library test.bad.fi0049;
type Foo = strict union {
// Strings can be optional in general, but not in unions.
1: bar string:optional;
};
Remove the optional
constraint:
library test.good.fi0049;
type Foo = strict union {
1: bar string;
};
FIDL does not allow union members to be optional because this can result in many ways of expressing the same value.
For example, a union with three optional members would have 6 states (2 per member). Instead, this should be modeled with a
fourth member whose type is struct {}
, or by making the overall union optional with Foo:optional
.
fi-0050: Deprecated struct default syntax prohibited
Previously, FIDL allowed for default values to be set on struct
members:
library test.bad.fi0050;
type MyStruct = struct {
field int64 = 20;
};
As of RFC-0160: Remove support for FIDL struct defaults, this behavior is disallowed:
library test.good.fi0050;
type MyStruct = struct {
field int64;
};
Default values for struct
members are no longer allowed. Users should set such
defaults in application logic instead.
A small number of legacy users of this syntax are allowed to continue using it behind an allowlist built into the compiler, but no new exceptions are being made to add to this list. As soon as these users are migrated off, this feature will be permanently removed from FIDL.
fi-0051: Unknown dependent library
This error occurs when you use a symbol that's from an unknown library.
library test.bad.fi0051;
type Company = table {
1: employees vector<unknown.dependent.library.Person>;
2: name string;
};
To fix this, import the missing dependent library with a using declaration.
library known.dependent.library;
type Person = table {
1: age uint8;
2: name string;
};
library test.good.fi0051;
using known.dependent.library;
type Company = table {
1: employees vector<known.dependent.library.Person>;
2: name string;
};
This error commonly occurs when the fidlc
command line invocation is
improperly formed. If you are confident that the unknown library exists and
should be resolvable, make sure you are passing the dependent library's files
correctly via a space-separated list passed to the --files
flag.
fi-0052: Name not found
This error occurs when you use a name that the FIDL compiler cannot find.
library test.bad.fi0052;
protocol Parser {
Tokenize() -> (struct {
tokens vector<string>;
}) error ParsingError; // ParsingError doesn't exist.
};
To fix this, either remove the name that's not found:
library test.good.fi0052a;
protocol Parser {
Tokenize() -> (struct {
tokens vector<string>;
});
};
or define the name that's not found:
library test.good.fi0052b;
type ParsingError = flexible enum {
UNEXPECTED_EOF = 0;
};
protocol Parser {
Tokenize() -> (struct {
tokens vector<string>;
}) error ParsingError;
};
fi-0053: Cannot refer to member
This error occurs when you refer to a member that's not a bits
or enum
entry.
library test.bad.fi0053a;
type Person = struct {
name string;
birthday struct {
year uint16;
month uint8;
day uint8;
};
};
const JOHNS_NAME Person.name = "John Johnson"; // Cannot refer to member of struct 'Person'.
library test.bad.fi0053b;
type Person = struct {
name string;
birthday struct {
year uint16;
month uint8;
day uint8;
};
};
type Cat = struct {
name string;
age Person.birthday; // Cannot refer to member of struct 'Person'.
};
To fix this error, either change to a named type:
library test.good.fi0053a;
type Person = struct {
name string;
birthday struct {
year uint16;
month uint8;
day uint8;
};
};
const JOHNS_NAME string = "John Johnson";
or extract the member's type:
library test.good.fi0053b;
type Date = struct {
year uint16;
month uint8;
day uint8;
};
type Person = struct {
name string;
birthday Date;
};
type Cat = struct {
name string;
age Date;
};
fi-0054: Invalid bits/enum member
This error occurs when an enum
or bits
member is referenced without being previously defined.
library test.bad.fi0054;
type Enum = enum {
foo_bar = 1;
};
const EXAMPLE Enum = Enum.FOO_BAR;
To avoid this error, confirm that you have previously declared a value for the referenced member value. These values are case sensitive.
library test.good.fi0054;
type Enum = enum {
foo_bar = 1;
};
const EXAMPLE Enum = Enum.foo_bar;
fi-0055: Invalid reference to deprecated
This error occurs when you use a reference to a type
or const
a with an
incompatible @available
attribute. This typically happens when using
deprecated types
or consts
from later versions.
@available(added=1)
library test.bad.fi0055;
@available(added=1, deprecated=2, note="use Color instead")
alias RGB = array<uint8, 3>;
@available(added=2)
type Color = struct {
r uint8;
g uint8;
b uint8;
a uint8;
};
@available(added=3)
type Config = table {
// RGB is deprecated in version 2.
1: color RGB;
};
To fix this error, use a non-deprecated type
or const
:
@available(added=1)
library test.good.fi0055;
@available(added=1, deprecated=2, note="use Color instead")
alias RGB = array<uint8, 3>;
@available(added=2)
type Color = struct {
r uint8;
g uint8;
b uint8;
a uint8;
};
@available(added=3)
type Config = table {
// Using a non-deprecated type.
1: color Color;
};
fi-0056: Invalid reference to deprecated other platform
This error occurs when you use a reference to a type
or const
from another
platform with an incompatible @available
attribute. This typically happens
when using deprecated types
or consts
from later versions.
@available(platform="foo", added=1)
library test.bad.fi0056a;
@available(added=1, deprecated=2, note="use Color instead")
alias RGB = array<uint8, 3>;
@available(added=2)
type Color = struct {
r uint8;
g uint8;
b uint8;
a uint8;
};
@available(platform="bar", added=2)
library test.bad.fi0056b;
using test.bad.fi0056a;
@available(added=3)
type Config = table {
// RGB is deprecated in version 2.
1: color test.bad.fi0056a.RGB;
};
To fix this error, use a non-deprecated type
or const
:
@available(platform="foo", added=1)
library test.good.fi0056a;
@available(added=1, deprecated=2, note="use Color instead")
alias RGB = array<uint8, 3>;
@available(added=2)
type Color = struct {
r uint8;
g uint8;
b uint8;
a uint8;
};
@available(platform="bar", added=2)
library test.good.fi0056b;
using test.good.fi0056a;
@available(added=2)
type Config = table {
// Change to use a non-deprecated type.
1: color test.good.fi0056a.Color;
};
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.fi0057c;
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.fi0057a;
type Yin = struct {
yang Yang;
};
type Yang = struct {
yin Yin;
};
library test.bad.fi0057b;
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.fi0057;
type MySelf = struct {
me box<MySelf>;
};
library test.bad.fi0057d;
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-0058: Reference to compiler generated payload name
Anonymous method payloads have their names automatically generated by the FIDL compiler, so that users of generated backend code may refer to the types they represent as necessary. Referring to these types in *.fidl files themselves, however, is forbidden:
library test.bad.fi0058;
protocol MyProtocol {
strict MyInfallible(struct {
in uint8;
}) -> (struct {
out int8;
});
strict MyFallible(struct {
in uint8;
}) -> (struct {
out int8;
}) error flexible enum {};
strict -> MyEvent(struct {
out int8;
});
};
type MyAnonymousReferences = struct {
a MyProtocolMyInfallibleRequest;
b MyProtocolMyInfallibleResponse;
c MyProtocolMyFallibleRequest;
d MyProtocol_MyFallible_Result;
e MyProtocol_MyFallible_Response;
f MyProtocol_MyFallible_Error;
g MyProtocolMyEventRequest;
};
If you wish to refer to the payload type directly, you should extract the payload type into its own named type declaration instead:
library test.good.fi0058;
type MyRequest = struct {
in uint8;
};
type MyResponse = struct {
out int8;
};
type MyError = flexible enum {};
protocol MyProtocol {
strict MyInfallible(MyRequest) -> (MyResponse);
strict MyFallible(MyRequest) -> (MyResponse) error MyError;
strict -> MyEvent(MyResponse);
};
type MyAnonymousReferences = struct {
a MyRequest;
b MyResponse;
c MyRequest;
// There is no way to explicitly name the error result union.
// d MyProtocol_MyFallible_Result;
e MyResponse;
f MyError;
g MyResponse;
};
All FIDL methods and events reserve the [PROTOCOL_NAME][METHOD_NAME]Request
name for their anonymous request payloads. Strict, infallible two-way methods
additionally reserve [PROTOCOL_NAME][METHOD_NAME]Response
. Two-way methods
that are flexible or fallible instead reserve:
[PROTOCOL_NAME]_[METHOD_NAME]_Result
[PROTOCOL_NAME]_[METHOD_NAME]_Response
[PROTOCOL_NAME]_[METHOD_NAME]_Error
These names use underscores unlike the others for historical reasons.
fi-0059: Invalid constant type
Not all types can used in const
declarations:
library test.bad.fi0059;
const MY_CONST string:optional = "foo";
Convert to an allowed type, if possible:
library test.good.fi0059;
const MY_CONST string = "foo";
Only FIDL primitives (bool
, int8
, int16
, int32
, int64
, uint8
,
uint16
, uint32
, uint64
, float32
, float64
) and non-optional string
types may be used in the left-hand side of a const
declaration.
fi-0060: Cannot resolve constant value
Constant values must be resolvable to known values:
library test.bad.fi0060;
const MY_CONST bool = optional;
Make sure that the constant being used is a valid value:
library test.good.fi0060;
const MY_CONST bool = true;
This error often accompanies other errors, which provide more information on the nature of the non-resolvable expected constant.
fi-0061: Or operator on non-primitive values
The binary-or operator can only be used on primitives:
library test.bad.fi0061;
const HI string = "hi";
const THERE string = "there";
const OR_OP string = HI | THERE;
Try representing the data being operated on as a bits
enumeration instead:
library test.good.fi0061;
type MyBits = flexible bits {
HI = 0x1;
THERE = 0x10;
};
const OR_OP MyBits = MyBits.HI | MyBits.THERE;
fi-0062: Newtypes are not allowed
Newtypes from RFC-0052: Type aliasing and new types are not fully implementented and cannot be used yet:
library test.bad.fi0062;
type Matrix = array<float64, 9>;
In the meantime, you can achieve something similar by defining a struct with a single element:
library test.good.fi0062a;
type Matrix = struct {
elements array<float64, 9>;
};
Alternatively, you can define an alias, but note that unlike a newtype this provides no type safety (that is, it can be used interchangeably with its underlying type):
library test.good.fi0062b;
alias Matrix = array<float64, 9>;
fi-0063: Expected value but got type
The right-hand side of a const
declaration must resolve to a constant value,
not a type:
library test.bad.fi0063;
type MyType = struct {};
const MY_CONST uint32 = MyType;
Ensure that the right-hand side is a value:
library test.good.fi0063;
const MY_VALUE uint32 = 8;
const MY_CONST uint32 = MY_VALUE;
fi-0064: Incorrect bits or enum value type
When using a bits
or enum
variant as the value in a const
declaration, the
type of the bits
/enum
value must be the same as the left-hand side of the
const declaration:
library test.bad.fi0064;
type MyEnum = enum : int32 {
VALUE = 1;
};
type OtherEnum = enum : int32 {
VALUE = 5;
};
const MY_CONST MyEnum = OtherEnum.VALUE;
One solution is to change the const
declaration's type to match that of the
value being stored:
library test.good.fi0064;
type MyEnum = enum : int32 {
VALUE = 1;
};
type OtherEnum = enum : int32 {
VALUE = 5;
};
const MY_CONST OtherEnum = OtherEnum.VALUE;
Alternatively, a different value can be selected to match the const
declaration's type:
library test.good.fi0064;
type MyEnum = enum : int32 {
VALUE = 1;
};
type OtherEnum = enum : int32 {
VALUE = 5;
};
const MY_CONST MyEnum = MyEnum.VALUE;
fi-0065: Cannot convert value to expected type
A constant value must be of a type appropriate for the location it is being used.
The most common cause for this error is when a const
declaration's value does
not match its stated type:
library test.bad.fi0065a;
const MY_CONST bool = "foo";
This can still be problematic when a correctly defined const
value is used in
a location where its underlying type is invalid:
library test.bad.fi0065b;
const ONE uint8 = 0x0001;
const TWO_FIFTY_SIX uint16 = 0x0100;
const TWO_FIFTY_SEVEN uint8 = ONE | TWO_FIFTY_SIX;
Additionally, FIDL's official check their arguments against a schema. Because these arguments are themselves constant values, the same kind of type mismatch can occur:
library test.bad.fi0065c;
protocol MyProtocol {
@selector(3840912312901827381273)
MyMethod();
};
In all of these cases, the solution is to use only use values of the expected
type in locations where const
values are accepted. The above cases become,
respectively:
library test.good.fi0065a;
const MY_CONST string = "foo";
library test.good.fi0065b;
const ONE uint8 = 0x0001;
const TWO_FIFTY_SIX uint16 = 0x0100;
const TWO_FIFTY_SEVEN uint16 = ONE | TWO_FIFTY_SIX;
library test.good.fi0065c;
protocol MyProtocol {
@selector("MyOldMethod")
MyMethod();
};
fi-0066: Constant overflows type
A constant value cannot fall outside of the range inherent to its underlying type:
library test.bad.fi0066;
const NUM uint64 = -42;
The problem may be fixed either by changing the value to fit within the type's range:
library test.good.fi0066a;
const NUM uint64 = 42;
Or otherwise by changing to the type to accommodate the currently overflowing value:
library test.good.fi0066b;
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.fi0067;
type NonPowerOfTwo = bits : uint64 {
THREE = 3;
};
Instead, member values should always be powers of two:
library test.good.fi0067a;
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.fi0067b;
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-0068: Flexible enums have a reserved unknown value
This error happens when you define an enum member whose value clashes with the reserved unknown value.
Flexible enums may hold values not known by the FIDL schema. In addition,
flexible enums always reserve some value which will be treated as unknown.
By default, that value is the maximum numerical value representable by the
underlying integer type of that enum (e.g. 255
in case of uint8
).
library test.bad.fi0068;
type Foo = flexible enum : uint8 {
ZERO = 0;
ONE = 1;
MAX = 255;
};
To fix the error, you may remove the member or change its value:
library test.good.fi0068a;
type Foo = flexible enum : uint8 {
ZERO = 0;
ONE = 1;
};
library test.good.fi0068b;
type Foo = flexible enum : uint8 {
ZERO = 0;
ONE = 1;
MAX = 254;
};
Finally, if you come across this error when transitioning a strict
enum to a
flexible
enum, you may use the @unknown
attribute to designate the numerical
value of a particular member as the unknown value. See @unknown
.
fi-0069: Bits must use unsigned integral subtype
Using signed numerics as the underlying type for a bits
declaration is
prohibited:
library test.bad.fi0069;
type Fruit = bits : int64 {
ORANGE = 1;
APPLE = 2;
BANANA = 4;
};
Instead, use any one of the following: uint8
, uint16
, uint32
, or uint64
:
library test.good.fi0069;
type Fruit = bits : uint64 {
ORANGE = 1;
APPLE = 2;
BANANA = 4;
};
Unlike enum
declarations, which allow both signed and unsigned integers (see:
fi-0070), bits
declarations only permit the latter. This is
because each bits
member necessarily represents a specific underlying bit in a
bit array (this is the reason that fi-0067
exists). This is most cleanly represented as a single unsigned integer. The
binary representation of unsigned integer maps directly to a single bit (2 to
the power of its index), whereas the negative numbers in signed integers almost
always select multiple bits due to the mechanics of the two's complement
representation.
fi-0070: Enum must use integral subtype
Using the non-integral numerics float32
or float64
as the underlying type
for an enum
declaration is prohibited:
library test.bad.fi0070;
type MyEnum = enum : float64 {
ONE_POINT_FIVE = 1.5;
};
Instead, use any one of the following: int8
, int16
,int32
,int64
, uint8
,
uint16
, uint32
, or uint64
:
library test.good.fi0070;
type MyEnum = enum : uint64 {
ONE = 1;
};
fi-0071: Unknown attribute disallowed on strict enum members
A strict enum
must not have any members annotated with the @unknown
attribute:
library test.bad.fi0071;
type MyEnum = strict enum : int8 {
@unknown
UNKNOWN = 0;
FOO = 1;
MAX = 127;
};
To continue using the @unknown
attribute, change to a flexible enum
:
library test.good.fi0071a;
type MyEnum = flexible enum : int8 {
@unknown
UNKNOWN = 0;
FOO = 1;
MAX = 127;
};
Otherwise, just remove the attribute altogether to remain a strict enum
:
library test.good.fi0071;
type MyEnum = strict enum : int8 {
UNKNOWN = 0;
FOO = 1;
MAX = 127;
};
The purpose of the @unknown
attribute is to
smooth over a transition from a strict enum
with a user-defined unknown value,
like this into a flexible enum
with unknown values known to and handled by
FIDL. In the above example, it would be used to transition from the second
correct usage to the first one.
fi-0072: Only enum member can carry the unknown attribute
Adorning multiple enum
members with the @unknown
attribute is prohibited:
library test.bad.fi0072;
type MyEnum = flexible enum : uint8 {
@unknown
UNKNOWN = 0;
@unknown
OTHER = 1;
};
Choose and annotate only the member that is used as a domain specific "unknown" value:
library test.good.fi0071a;
type MyEnum = flexible enum : int8 {
@unknown
UNKNOWN = 0;
OTHER = 1;
};
The purpose of the @unknown
attribute is to
smooth over a transition from a strict enum
with a user-defined unknown value,
like this into a flexible enum
with unknown values known to and handled by
FIDL:
library test.good.fi0072;
type MyEnum = strict enum : int8 {
UNKNOWN = 0;
OTHER = 1;
};
library test.good.fi0071a;
type MyEnum = flexible enum : int8 {
@unknown
UNKNOWN = 0;
OTHER = 1;
};
fi-0073: Composing non-protocol
Only protocols can be used in compose
statements:
library test.bad.fi0073;
type MyStruct = struct {};
protocol MyProtocol {
compose MyStruct;
};
Make sure the name you are referring to points to a protocol:
library test.good.fi0073;
protocol MyOtherProtocol {};
protocol MyProtocol {
compose MyOtherProtocol;
};
fi-0074: Invalid layout used for method payload
Only struct
, table
, or union
layouts may be used to describe method
payloads:
library test.bad.fi0074;
protocol MyProtocol {
MyMethod(enum {
FOO = 1;
});
};
Use one of those layouts instead:
library test.good.fi0074;
protocol MyProtocol {
MyMethod(struct {
foo bool;
});
};
fi-0075: Invalid primitive used for method payload
Primitives cannot be used as method method payload:
library test.bad.fi0075;
protocol MyProtocol {
MyMethod(uint32);
};
Use a type of the struct
, table
, or union
layout instead:
library test.good.fi0075;
protocol MyProtocol {
MyMethod(struct {
wrapped_in_struct uint32;
});
};
For cases where the desirable payload really is just a primitive value, and
future evolution is not a concern, wrapping the value in a struct
layout will
result in a payload that is the same size as desired value is by itself.
fi-0076
fi-0077: Interaction payload cannot be empty struct
The payloads in a method or event cannot be empty structs:
library test.bad.fi0077a;
protocol Test {
MyMethod(struct {}) -> (struct {});
};
library test.bad.fi0077b;
protocol Test {
-> MyEvent(struct {});
};
If you would like to express that a particular request/response does not hold
any information, delete the empty struct, leaving ()
in that location:
library test.good.fi0077a;
protocol Test {
MyMethod() -> ();
};
library test.good.fi0077b;
protocol Test {
-> MyEvent();
};
Empty structs cannot be extended, and take 1 byte on the wire. Since FIDL supports interactions without payloads, using empty structs this way is superfluous and less efficient. Therefore they are not allowed.
fi-0078
fi-0079
fi-0080: Generated zero value ordinal
This error should never occur. If you managed to make it happen, congratulations, you've probably broken SHA-256!
Joking aside, this error occurs if the fidlc compiler generates an ordinal value of 0. It should never happen, so if it does, you've probably found a bug in the FIDL compiler. Please report the issue to our issue tracker if this happens.
fi-0081: Duplicate method ordinal
This error usually occurs when you use an @selector
attribute
to make two method names produce the same ordinal.
library test.bad.fi0081;
protocol Parser {
ParseLine();
// Multiple methods with the same ordinal...
@selector("ParseLine")
ParseOneLine();
};
To fix this issue, update either the method names, or selectors to not collide.
library test.good.fi0081;
protocol Parser {
ParseLine();
@selector("Parse1Line")
ParseOneLine();
};
This error can also happen if there is a SHA-256 collision, but the chances of this are basically zero. If you are positive your selectors aren't at fault and you still run into this error, you've probably found a bug in the FIDL compiler. Please report the issue to our issue tracker if this happens.
fi-0082: Invalid selector value
This error occurs when you use an invalid value for an @selector. Most commonly, this is due to a typo. A selector must either be a standalone method name, or a fully qualified method name.
library test.bad.fi0082;
protocol Parser {
@selector("test.old.fi0082.Parser.Parse")
Parse();
};
To fix this, update the selector to either be a valid standalone, or fully qualified method name:
library test.good.fi0082;
protocol Parser {
@selector("test.old.fi0082/Parser.Parse")
Parse();
};
fi-0083: fuchsia.io
must use explicit ordinals
The FIDL compiler used to automatically rename fuchsia.io
ordinals to
fuchsia.io1
. This magic was intended to make it easier to migrate to
fuchsia.io2
by letting the io2
versions of the methods have the "normal"
ordinal. However, this system ended up being a bit too magical so it is now
required to manually provide the ordinal for fuchsia.io
.
library fuchsia.io;
protocol SomeProtocol {
SomeMethod();
};
To fix this issue, manually provide a selector using fuchsia.io1
as the
library name to allow the fuchsia.io
names to be used for io2.
library fuchsia.io;
protocol SomeProtocol {
@selector("fuchsia.io1/SomeProtocol.SomeMethod")
SomeMethod();
};
fi-0084: Default members disallowed on method payload structs
Structs used as method paylods may not specify default members:
library test.bad.fi0084;
type MyStruct = struct {
@allow_deprecated_struct_defaults
a bool = false;
};
protocol MyProtocol {
MyMethod(MyStruct) -> (MyStruct);
};
Remove the default members from the relevant struct
declaration:
library test.good.fi0084;
type MyStruct = struct {
a bool;
};
protocol MyProtocol {
MyMethod(MyStruct) -> (MyStruct);
};
fi-0085
fi-0086
fi-0087
fi-0088: Service members cannot be optional
This error occurs when you mark a service member as optional
. Marking a
service member as optional
isn't allowed because service members are always
optional.
library test.bad.fi0088;
protocol Sorter {
Sort(struct {
input vector<int32>;
}) -> (struct {
output vector<int32>;
});
};
service SortService {
quicksort client_end:<Sorter, optional>;
mergesort client_end:<Sorter, optional>;
};
To fix this, remove the optional clause:
library test.good.fi0088;
protocol Sorter {
Sort(struct {
input vector<int32>;
}) -> (struct {
output vector<int32>;
});
};
service SortService {
quicksort client_end:Sorter;
mergesort client_end:Sorter;
};
fi-0089
fi-0090
fi-0091: Invalid struct member type
This error occurs when you try to set a default struct value for a non-supported type. Only numeric and boolean types are allowed to set a default struct value.
library test.bad.fi0091;
type Person = struct {
@allow_deprecated_struct_defaults
name string:optional = "";
};
To fix this, remove the default value:
library test.good.fi0091;
type Person = struct {
@allow_deprecated_struct_defaults
name string:optional;
};
fi-0092: Table ordinal too large
FIDL table ordinals cannot be greater than 64:
library test.bad.fi0092;
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;
// The 64th field of a table must be another table, otherwise it will cause
// fi-0093: Max Ordinal In Table Must Be Table.
64: v64 Table64thField;
65: v65 int64;
};
In order to allow growth beyond 64 ordinals, FIDL requires the last field of a table to be another table. Any table fields beyond 64 must be placed in a nested table.
library test.good.fi0092;
type Table64thField = table {
1: x int64;
// Any fields beyond 64 of table Example must be move to the nested table in
// ordinal 64 of Example.
2: v65 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;
};
Every field in a table incurs the overhead of a FIDL envelope in order to allow the field to be optional. This allows every field of a table to be present or absent and enables tables to be evolved by adding or removing fields, but at the cost of much greater memory overhead than structs.
In general you can both avoid this error and reduce overhead by avoiding having small, granular fields in tables. Instead you can group together elements that you expect will need to be added or removed simultaneously into structs and use those as the fields of the table. This reduces overhead and avoid running out of ordinals at the cost of some evolvability.
This became an error in RFC-0132: FIDL table size limit, which was intended to prevent users from accidentally incurring the overhead of very large tables. This extra cost isn't obvious in the schema, especially when there are only a few fields (with large ordinals), or where there are many fields but only a few are used at a time.
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.fi0093;
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.fi0093;
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-0094: Duplicate table member ordinals
The ordinals used for members in a table
declaration cannot be repeated:
library test.bad.fi0094;
type MyTable = table {
1: my_field string;
1: my_other_field uint32;
};
Increment the ordinals as needed to ensure that the declaration has unique ordinals for all of its members:
library test.good.fi0094a;
type MyTable = table {
1: my_field string;
2: my_other_field uint32;
};
Alternatively, one of the members with the duplicated name can be removed:
library test.good.fi0094b;
type MyTable = table {
1: my_field string;
};
The ordinal is used to identify the field on the wire. If two members share an ordinal, there is no reliable way to tell which field is being referred to when decoding a FIDL message.
fi-0095
fi-0096
fi-0097: Duplicate union member ordinals
The ordinals used for members in a union
declaration cannot be repeated:
library test.bad.fi0097;
type MyUnion = strict union {
1: my_variant string;
1: my_other_variant int32;
};
Increment the ordinals as needed to ensure that the declaration has unique ordinals for all of its members:
library test.good.fi0097a;
type MyUnion = strict union {
1: my_variant string;
2: my_other_variant int32;
};
Alternatively, one of the members with the duplicated name can be removed:
library test.good.fi0097b;
type MyUnion = strict union {
1: my_variant string;
};
The ordinal is used to identify the variant on the wire. If two members share an ordinal, there is no reliable way to tell which variant is being referred to when decoding a FIDL message.
fi-0098
fi-0099
fi-0100
fi-0101: Unresolvable size constraint
The size constraint applied to a vector
or string
type definition must be a
valid value of the uint32
type:
library test.bad.fi0101a;
alias MyBoundedOptionalVector = vector<uint32>:<"255", optional>;
library test.bad.fi0101b;
alias MyBoundedOptionalVector = vector<uint32>:<uint8, optional>;
Ensure that this is the case:
library test.good.fi0101;
alias MyBoundedOptionalVector = vector<uint32>:<255, optional>;
fi-0102: Unresolvable member value
The members of bits
and enum
declarations must be resolvable values of the
specified subtype:
library test.bad.fi0102;
type Fruit = bits : uint64 {
ORANGE = 1;
APPLE = 2;
BANANA = -4;
};
Ensure that all values match the underlying type of the declaration:
library test.good.fi0102;
type Fruit = bits : uint64 {
ORANGE = 1;
APPLE = 2;
BANANA = 4;
};
fi-0103: Unresolvable struct default value
The default values for the members of struct
declarations must match their
respective member's stated type:
library test.bad.fi0103;
type MyEnum = enum : int32 {
A = 1;
};
type MyStruct = struct {
@allow_deprecated_struct_defaults
field MyEnum = 1;
};
Ensure that the value matches the declared type:
library test.good.fi0103;
type MyEnum = enum : int32 {
A = 1;
};
type MyStruct = struct {
@allow_deprecated_struct_defaults
field MyEnum = MyEnum.A;
};
fi-0104: Unresolvable attribute argument
Values of arguments for official FIDL attributes cannot be invalid per the attribute schema's expectation for that argument:
library test.bad.fi0104;
type MyStruct = struct {
my_field @generated_name(true) struct {};
};
Ensure that the type of value being used as an attribute argument is correct:
library test.good.fi0104;
type MyStruct = struct {
my_field @generated_name("my_inner_type") struct {};
};
fi-0105
fi-0106
fi-0107: Duplicate member values
Neither bits
nor enum
declarations can have members with the same value:
library test.bad.fi0107;
type Fruit = flexible enum {
ORANGE = 1;
APPLE = 1;
};
Change the member values to all be unique:
library test.good.fi0107a;
type Fruit = flexible enum {
ORANGE = 1;
APPLE = 2;
};
Alternatively, remove one of the duplicated members:
library test.good.fi0107b;
type Fruit = flexible enum {
ORANGE = 1;
};
fi-0108
fi-0109
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.fi0110;
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.fi0110a;
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.fi0110b;
type Foo = struct {
value uint32;
};
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-0111: Inline size exceeds limit
FIDL types whose inline size is 64 KiB or more are not allowed:
library test.bad.fi0111;
type MyStruct = struct {
numbers array<uint8, 65536>;
};
Instead, make sure the type has an inline size less than 64 KiB. In this case, we can adjust the array bound:
library test.good.fi0111;
type MyStruct = struct {
numbers array<uint8, 65535>;
};
This limit exists for performance reasons. It means that encoders and decoders can assume sizes and offsets fit in unsigned 16 bit integers.
You are unlikely to run into this in practice unless you use large arrays or deeply nested structs. Most FIDL constructs (such as strings, vectors, tables, and unions) use out-of-line storage, which does not count towards their individual inline size.
fi-0112: Service member is not a client_end
Service members are only allowed to be client ends, not any other type:
library test.bad.fi0112;
protocol Calculator {};
service Service {
calculator server_end:Calculator;
};
To fix the error, make sure the member has the form client_end:P
for some protocol P
:
library test.good.fi0112;
protocol Calculator {};
service Service {
calculator client_end:Calculator;
};
A service is a collection of protocol instances, not a general purpose data structure, so it would not make sense for one to allow arbitrary types.
fi-0113: Mismatched transport in service
A FIDL service is not allowed to contain protocols using different transports:
library test.bad.fi0113;
protocol ChannelProtocol {};
@transport("Driver")
protocol DriverProtocol {};
service SomeService {
a client_end:ChannelProtocol;
b client_end:DriverProtocol;
};
Instead, use separate services for each transport:
library test.good.fi0113;
protocol ChannelProtocol {};
@transport("Driver")
protocol DriverProtocol {};
service ChannelService {
protocol client_end:ChannelProtocol;
};
service DriverService {
protocol client_end:DriverProtocol;
};
Note that services are an unfinished feature in FIDL. They were originally designed in RFC-0041: Support for unifying serviceas and devices. See https://fxbug.dev/42160684 for the status as of October 2022.
fi-0114: Composed protocol is too open
A protocol cannot compose another protocol that is more open than itself:
library test.bad.fi0114;
open protocol Composed {};
ajar protocol Composing {
compose Composed;
};
You can fix this by increasing the openness of the composing protocol, i.e.
changing it from closed
to ajar
or from ajar
to open
:
library test.good.fi0114a;
open protocol Composed {};
open protocol Composing {
compose Composed;
};
Alternatively, you can reduce the openness of the composed protocol, i.e. change
it from open
to ajar
or from ajar
to closed
:
library test.good.fi0114b;
ajar protocol Composed {};
ajar protocol Composing {
compose Composed;
};
This rule exists because the openness of a protocol restricts what kind of methods it is allowed to contain. For example, an ajar protocol cannot contain flexible two-way methods, but an open protocol can, so it's not safe for an ajar protocol to compose an open protocol.
See RFC-0138: Handling unknown interactions for more information about protocol modifiers.
fi-0115: Flexible two-way method in requires open protocol
Closed and ajar protocols are not allowed to contain flexible two-way methods:
library test.bad.fi0115;
ajar protocol Protocol {
flexible Method() -> ();
};
Instead, mark the two-way method strict
instead of flexible
:
library test.good.fi0115a;
ajar protocol Protocol {
strict Method() -> ();
};
Alternatively, mark the protocol open
instead of closed
or ajar
:
library test.good.fi0115b;
open protocol Protocol {
flexible Method() -> ();
};
This error exists because the purpose of the closed
(or ajar
) modifier is to
make sure a method does not contain any flexible (two-way) methods. When first
creating a protocol, you should carefully think about whether it should be
closed, ajar, or open based on the evolvability properties you need from it.
See RFC-0138: Handling unknown interactions for more information about protocol modifiers.
fi-0116: Flexible one-way method requires ajar or open protocol
Closed protocols are not allowed to contain flexible one-way methods:
library test.bad.fi0116;
closed protocol Protocol {
flexible Method();
};
Instead, mark the one-way method strict
instead of flexible
:
library test.good.fi0116;
closed protocol Protocol {
strict Method();
};
Alternatively, mark the protocol ajar
or open
instead of closed
:
library test.good.fi0116;
ajar protocol Protocol {
flexible Method();
};
This error exists because the purpose of the closed
modifier is to make sure a
method does not contain any flexible methods. When first creating a protocol,
you should carefully think about whether it should be closed, ajar, or open
based on the evolvability properties you need from it.
See RFC-0138: Handling unknown interactions for more information about protocol modifiers.
fi-0117: Handle used in incompatible transport
Protocols can only refer to handles that are compatible with their transport. For example, a protocol over the Zircon channel transport cannot refer to Fuchsia Driver Framework handles:
library test.bad.fi0117;
using fdf;
protocol Protocol {
Method(resource struct {
h fdf.handle;
});
};
Instead, use handles that are compatible with the protocol's transport:
library test.good.fi0117a;
using zx;
protocol Protocol {
Method(resource struct {
h zx.Handle;
});
};
Alternatively, change the transport of the protocol to match the handles:
library test.good.fi0117b;
using fdf;
@transport("Driver")
protocol Protocol {
Method(resource struct {
h fdf.handle;
});
};
fi-0118: Transport end used in incompatible transport
Protocols can only refer to transport ends (client_end
and server_end
) of
protocols over the same transport. For example, a protocol using the Syscall
transport cannot refer to a client end of a protocol using the Driver transport:
library test.bad.fi0118;
@transport("Driver")
protocol DriverProtocol {};
@transport("Syscall")
protocol P {
M(resource struct {
s client_end:DriverProtocol;
});
};
To fix the error, remove the transport end member:
library test.good.fi0118;
@transport("Driver")
protocol DriverProtocol {};
@transport("Syscall")
protocol Protocol {
M();
};
fi-0119
fi-0120: Invalid attribute placement
Some official attributes are only
allowed in certain places. For example, the @selector
attribute can only be
used on methods:
library test.bad.fi0120a;
@selector("Nonsense")
type MyUnion = union {
1: hello uint8;
};
To fix the error, remove the attribute:
library test.good.fi0120a;
type MyUnion = union {
1: hello uint8;
};
You might also encounter this error when intending to use an attribute in a way
that is supported, but placing it in the wrong spot. For example, the
@generated_name
attribute cannot go directly on a member:
library test.bad.fi0120a;
@selector("Nonsense")
type MyUnion = union {
1: hello uint8;
};
Instead, it should go just before the member's anonymous layout:
library test.good.fi0120a;
type MyUnion = union {
1: hello uint8;
};
fi-0121: Deprecated attribute
Some official attributes are deprecated and should no longer be used:
library test.bad.fi0121;
@example_deprecated_attribute
type MyStruct = struct {};
The fix depends on why the attribute was deprecated. For example, the error message might say to use a different attribute instead. In this case, we can just remove the attribute:
library test.good.fi0121;
type MyStruct = struct {};
fi-0122: Duplicate attribute name
An element cannot have multiple attributes with the same name:
library test.bad.fi0122;
@custom_attribute("first")
@custom_attribute("second")
type Foo = struct {};
Instead, specify each attribute only once:
library test.good.fi0122;
@custom_attribute("first")
type Foo = struct {};
fi-0123: Duplicate canonical attribute name
An element cannot have multiple attributes with the same canonical name:
library test.bad.fi0123;
@custom_attribute("first")
@CustomAttribute("second")
type Foo = struct {};
Even though custom_attribute
and CustomAttribute
look different, they are
both represented by the canonical name custom_attribute
. You get the
canonical name by converting the original name to snake_case
.
To fix the error, give each attribute a name that is unique after canonicalization.
library test.good.fi0123;
@custom_attribute("first")
@AnotherCustomAttribute("first")
type Foo = struct {};
See fi-0035 for more details on why FIDL requires declarations to have unique canonical names.
fi-0124: Custom attribute argument must be string or bool
The arguments on user-defined FIDL attributes are limited to being string or boolean types:
library test.bad.fi0124;
@my_custom_attr(foo=1, bar=2.3)
type MyStruct = struct {};
library test.good.fi0124;
@my_custom_attr(foo=true, bar="baz")
type MyStruct = struct {};
Unlike official attributes, the schema of user defined
attributes is not known to the compiler. Because of this, it is impossible for
the compiler to deduce the type of any given numeric argument - is 2
an
int8
, a uint64
, or a float32
? There's no way for the compiler to know.
A possible solution to this problem would be to implement a first-class
numeric
type in the JSON IR for ambiguous cases like these. However, because
custom attribute arguments are the only known use case for this today, this
feature has not been prioritized.
fi-0125: Attribute argument must not be named
When using an official attribute that takes a single argument, you cannot name that argument:
library test.bad.fi0125;
@transport(value="Driver")
protocol Foo {};
Instead, pass the argument without giving it a name:
library test.good.fi0125;
@discoverable(name="example.Bar")
protocol Foo {};
FIDL enforces this to make attributes more concise and consistent. Under the
hood, the argument name is inferred to be value
(and this will show up in the
JSON IR) because that is the only argument the attribute takes.
fi-0126: Attribute argument must be named
When using an official attribute that takes multiple arguments, you cannot pass an unnamed argument:
@available(1)
library test.bad.fi0126;
Instead, specify the name of the argument:
@available(added=1)
library test.good.fi0126;
This error occurs because there is no way of knowing what argument you intended to set if the attribute accepts more than one argument.
fi-0127: Missing required attribute argument
When using an official attribute that has a required argument, you cannot omit it:
library test.bad.fi0127;
@has_required_arg
type Foo = struct {};
Instead, provide the required argument:
library test.good.fi0127;
@has_required_arg(required="something")
type Foo = struct {};
fi-0128: Missing single attribute argument
When using an official attribute that requires a single argument, you cannot omit it:
library test.bad.fi0128;
@transport
protocol Protocol {};
Instead, provide an argument:
library test.good.fi0128;
@transport("Driver")
protocol Protocol {};
fi-0129: Unknown attribute argument
When using an official attribute, you cannot provide an argument that is not in its schema:
@available(added=1, discontinued=2)
library test.bad.fi0129;
If you meant to pass a different argument and got the name wrong, change it to use the correct name:
@available(added=1, deprecated=2)
library test.good.fi0129a;
Alternatively, remove the argument:
@available(added=1)
library test.good.fi0129b;
This error occurs because official attributes are validated against a schema. If FIDL allowed arbitrary arguments, they would have no effect, and it could cause bugs by masking typos.
fi-0130: Duplicate attribute argument
An attribute cannot have two arguments with the same name:
library test.bad.fi0130;
@custom_attribute(custom_arg=true, custom_arg=true)
type Foo = struct {};
Instead, only provide one argument with that name:
library test.good.fi0130;
@custom_attribute(custom_arg=true)
type Foo = struct {};
fi-0131: Duplicate canonical attribute argument
An attribute cannot have two arguments with the same canonical name:
library test.bad.fi0131;
@custom_attribute(custom_arg=true, CustomArg=true)
type Foo = struct {};
Even though custom_arg
and CustomArg
look different, they are both
represented by the canonical name custom_arg
. You get the canonical name
by converting the original name to snake_case
.
To fix the error, give each argument a name that is unique after canonicalization:
library test.good.fi0131a;
@custom_attribute(custom_arg=true, AnotherCustomArg=true)
type Foo = struct {};
Alternatively, remove one of the arguments:
library test.good.fi0131b;
@custom_attribute(custom_arg=true)
type Foo = struct {};
See fi-0035 for more details on why FIDL requires declarations to have unique canonical names.
fi-0132: Unexpected attribute argument
When using an official attribute that takes no arguments, you cannot provide an argument:
library test.bad.fi0132;
type Foo = flexible enum : uint8 {
@unknown("hello")
BAR = 1;
};
Instead, remove the argument:
library test.good.fi0132;
type Foo = flexible enum : uint8 {
@unknown
BAR = 1;
};
fi-0133: Attribute argument must be literal
Certain official attributes do not allow arguments that are references to constants:
library test.bad.fi0133;
const NAME string = "MyTable";
type Foo = struct {
bar @generated_name(NAME) table {};
};
Instead, pass a literal value as the argument:
library test.good.fi0133;
type Foo = struct {
bar @generated_name("MyTable") table {};
};
These attributes require a literal argument because their values influence compilation. Supporting non-literal arguments would be difficult to implement, or in some cases impossible because it leads to contradictions.
fi-0134
fi-0135: Invalid discoverable name
This error occurs when you use a bad name for an @discoverable
attribute.
@discoverable
attributes should be the library name followed by a .
and the
protocol name.
library test.bad.fi0135;
@discoverable(name="test.bad.fi0135/Parser")
protocol Parser {
Tokenize() -> (struct {
tokens vector<string>;
});
};
To fix this error, use a valid discoverable name:
library test.good.fi0135;
@discoverable(name="test.good.fi0135.Parser")
protocol Parser {
Tokenize() -> (struct {
tokens vector<string>;
});
};
fi-0136
fi-0137
fi-0138
fi-0139
fi-0140
fi-0141: Invalid error type
The error
type on a method response payload must be an int32
, uint32
, or
enum
thereof:
library test.bad.fi0141;
protocol MyProtocol {
MyMethod() -> () error float32;
};
Change the error
type to one of the valid options to fix this error:
library test.good.fi0141;
protocol MyProtocol {
MyMethod() -> () error int32;
};
See RFC-0060: Error handling for more details.
fi-0142: Invalid protocol transport type
The @transport(...)
attribute on a protocol
declaration must not specify an
invalid transport:
library test.bad.fi0142;
@transport("Invalid")
protocol MyProtocol {
MyMethod();
};
Use one of the supported transports instead:
library test.good.fi0142;
@transport("Syscall")
protocol MyProtocol {
MyMethod();
};
What constitutes a supported transport is still being finalized. See the FIDL attributes for the latest information.
fi-0143
fi-0144
fi-0145: Attribute typo
An attribute name whose spelling is too similar to one of FIDL's official attributes will result in a compiler warning:
library test.bad.fi0145;
@duc("should be doc")
protocol Example {
Method();
};
In the above example, the attribute @duc
is too similar in spelling to the
official FIDL attribute @doc
. In cases like this, the attribute naming is
intentional, and not an accidental misspelling of an official FIDL attribute, it
should be modified to be sufficiently unique:
library test.good.fi0145;
@duck("quack")
protocol Example {
Method();
};
Besides spell checking, the purpose of this warning is to discourage using names that are too similar to official FIDL attributes.
The typo detection algorithm works by calculating the edit distance of the attribute name from every official FIDL attribute. Names that are too similar, defined as having too small of an edit distance, trigger the typo detector.
fi-0146: Invalid generated name
This error occurs when you use the @generated_name
attribute with an invalid
name. Generated names must follow the same rules as all FIDL identifiers.
library test.bad.fi0146;
type Device = table {
1: kind flexible enum {
DESKTOP = 1;
PHONE = 2;
};
};
type Input = table {
1: kind @generated_name("_kind") flexible enum {
KEYBOARD = 1;
MOUSE = 2;
};
};
To fix this issue, change the @generated_name
value to a valid identifier.
library test.good.fi0146;
type Device = table {
1: kind flexible enum {
DESKTOP = 1;
PHONE = 2;
};
};
type Input = table {
1: kind @generated_name("input_kind") flexible enum {
KEYBOARD = 1;
MOUSE = 2;
};
};
fi-0147: @available
missing arguments
This error occurs when you use the @available
attribute and don't provide the
necessary arguments. @available
requires at least one of added
,
deprecated
, or removed
.
@available(added=1)
library test.bad.fi0147;
@available
type Foo = struct {};
To fix this issue, add one of the required arguments:
@available(added=1)
library test.good.fi0147;
@available(added=2)
type Foo = struct {};
See FIDL versioning for more information.
fi-0148: Note without deprecation
This errors occurs when you use the note
argument for the @available
attribute without the deprecated
argument. note
s are only supported for
deprecation.
@available(added=1, note="My note")
library test.bad.fi0148;
To fix this error, either remove the note:
@available(added=1)
library test.good.fi0148a;
or add the necessary deprecated
notice:
@available(added=1, deprecated=2, note="Removed in 2; use X instead.")
library test.good.fi0148b;
See FIDL versioning for more information.
fi-0149: Platform not on library
This error occurs when you try to use the @available
attribute's platform
argument anywhere other than above a library declaration. The platform
argument is only valid at the library
level.
@available(added=1)
library test.bad.fi0149;
@available(platform="foo")
type Person = struct {
name string;
};
To fix this, either move the platform
argument to the library @available
attribute:
@available(added=1, platform="foo")
library test.good.fi0149a;
type Person = struct {
name string;
};
or remove the platform
argument completely:
@available(added=1)
library test.good.fi0149b;
type Person = struct {
name string;
};
fi-0150: Library availability missing added
This error occurs when you add an @available
attribute to your library without
providing an added
argument. The added
argument is required for library
@available
attributes.
@available(removed=2)
library test.bad.fi0150a;
@available(platform="foo")
library test.bad.fi0150b;
To fix this issue, add the added
argument to the library's @available
attribute:
@available(added=1, removed=2)
library test.good.fi0150a;
@available(added=1, platform="foo")
library test.good.fi0150b;
fi-0151: Missing library availability
This error occurs when you add an @available
attribute on a non-library
declaration without an @available
attribute on the library
declaration.
library test.bad.fi0151;
@available(added=1)
type Person = struct {
name string;
};
To fix this error, add the @available
attribute to the library
declaration:
@available(added=1)
library test.good.fi0151a;
@available(added=1)
type Person = struct {
name string;
};
or remove the @available
attribute from the nonlibrary
declaration:
library test.good.fi0151b;
type Person = struct {
name string;
};
fi-0152: Invalid platform
This error occurs when you use invalid characters for the platform
argument of
an @available
attribute. The platform
argument must be a valid FIDL library
identifier.
@available(added=1, platform="Spaces are not allowed")
library test.bad.fi0152;
To fix this error, remove the disallowed characters:
@available(added=1, platform="foo")
library test.good.fi0152;
fi-0153: Invalid version
This error occurs when you use an invalid version for an added
or removed
argument on an @available
attribute. added
and removed
arguments must be
positive integers between 1 and 2^63-1, or the special constant HEAD
.
@available(added=0)
library test.bad.fi0153;
To fix this issue, change the version to a valid value:
@available(added=1)
library test.good.fi0153;
fi-0154: Invalid availability order
This error occurs when you use a bad combination of added
, deprecated
, and
remove
arguments for an @available
attribute. The following constraints must
be respected:
added
must be less than or equal todeprecated
deprecated
must be less thanremoved
added
must be less thanremoved
@available(added=2, removed=2)
library test.bad.fi0154a;
@available(added=2, deprecated=3, removed=3)
library test.bad.fi0154b;
To fix this issue update the added
, deprecated
, and removed
arguments to
the required ordering:
@available(added=1, removed=2)
library test.good.fi0154a;
@available(added=2, deprecated=2, removed=3)
library test.good.fi0154b;
fi-0155: Availability conflicts with parent
This error occurs when you add an @availability
attribute to a non-library
declaration that conflicts with the library
's declaration.
@available(added=2, deprecated=3, removed=4)
library test.bad.fi0155a;
@available(added=1)
type Person = struct {
name string;
};
@available(added=2, deprecated=3, removed=4)
library test.bad.fi0155b;
@available(added=4)
type Person = struct {
name string;
};
To fix this error, update the @availability
attributes to the required
constraints:
@available(added=2, deprecated=3, removed=4)
library test.good.fi0155;
@available(added=2)
type Person = struct {
name string;
};
fi-0156: Cannot be optional
This error occurs when you try to mark a type as optional that cannot be optional.
library test.bad.fi0156;
type Person = struct {
name string;
age int16:optional;
};
To fix this error, remove the optional constraint:
library test.good.fi0156;
type Person = struct {
name string;
age int16;
};
Only FIDL types that can be made optional with no change to the wire shape are
allowed to use the optional
constraint. See the
optionality guide, or the expandable below, for more information.
FIDL recipe: Optionality
Certain FIDL types can be made optional with no change to the wire shape of
their containing message with the addition of the :optional
constraint.
Further, the table
layout is always optional, while the struct
layout never
is. To make a struct
optional, it must be wrapped in a box<T>
, thereby
changing the wire shape of its containing message.
Base type | Optional version | Does optionality change the wire layout? |
---|---|---|
struct {...} |
box<struct {...}> |
Yes |
table {...} |
table {...} |
No |
union {...} |
union {...}:optional |
No |
vector<T> |
vector<T>:optional |
No |
string |
string:optional |
No |
zx.Handle |
zx.Handle:optional |
No |
client_end:P |
client_end:<P, optional> |
No |
server_end:P |
server_end:<P, optional> |
No |
All other types (bits
, enum
, array<T, N>
, and the primitive types) cannot
be made optional.
In this variant, we allow our key-value store to take other key-value stores as
members. In short, we turn it into a tree. We do this by replacing the original
definition of value
with one that utilizes a two-member union
: one variant
stores leaf nodes using the same vector<byte>
type as before, while the other
stores branch nodes in the form of other nested stores.
Reasoning
Here, we see several uses of optionality, whereby we can declare a type that may or may not exist. There are three flavors of optionality in FIDL:
- Types that have are always stored
out-of-line
on the wire, and thus have a builtin way to describe "absentness" via the
null envelope. Enabling
optionality for these types doesn't affect the wire shape of messages they are
included in - it simply changes which values are valid for that particular
type. The
union
,vector<T>
,client_end
,server_end
, andzx.Handle
types can all be made optional via the addition of the:optional
constraint. By making ourvalue
union
optional, we are able to introduce a canonical "null" entry, in the form of an absentvalue
. This means that emptybytes
and absent/emptystore
properties are invalid values. - Unlike the aforementioned types, the
struct
layout has no extra space where a null header can be stored. Because of this, it needs to be wrapped in an envelope, changing the on-the-wire shape of the message it is being included in. To ensure that this wire-modifying effect easily legible, theItem
struct
type must be wrapped in abox<T>
type template. - Finally,
table
layouts are always optional. An absenttable
is simply one with none of its members set.
Trees are a naturally self-referential data structure: any node in the tree may
contain a leaf with pure data (in this case, a string), or a sub-tree with more
nodes. This requires recursion: the definition of Item
is now transitively
dependent on itself! Representing recursive types in FIDL can be a bit tricky,
especially because support is currently somewhat
limited. We can support such types as long as there is
at least one optional type in the cycle created by the self-reference. For
instance, here we define the items
struct
member to be a box<Item>
,
thereby breaking the includes cycle.
These changes also make heavy use of anonymous types, or types whose
declarations are inlined at their sole point of use, rather than being named,
top-level type
declarations of their own. By default, the names of anonymous
types in the generated language bindings are taken from their local context. For
instance, the newly introduced flexible union
takes on its owning member's
name Value
, the newly introduced struct
would become Store
, and so on.
Because this heuristic can sometimes cause collisions, FIDL provides an escape
hatch by allowing the author to manually override an anonymous type's generated
name. This is done via the @generated_name
attribute, which allows one to
change the name generated by backends. We can use one here, where the would-be
Store
type is renamed to NestedStore
to prevent a name collision with the
protocol
declaration that uses that same name.
Implementation
The FIDL, CML, and realm interface definitions are modified as follows:
FIDL
// Copyright 2022 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. library examples.keyvaluestore.supporttrees; /// An item in the store. The key must match the regex `^[A-z][A-z0-9_\.\/]{2,62}[A-z0-9]$`. That /// is, it must start with a letter, end with a letter or number, contain only letters, numbers, /// periods, and slashes, and be between 4 and 64 characters long. type Item = struct { key string:128; value strict union { // Keep the original `bytes` as one of the options in the new union. All leaf nodes in the // tree must be `bytes`, or absent unions (representing empty). Empty byte arrays are // disallowed. 1: bytes vector<byte>:64000; // Allows a store within a store, thereby turning our flat key-value store into a tree // thereof. Note the use of `@generated_name` to prevent a type-name collision with the // `Store` protocol below, and the use of `box<T>` to ensure that there is a break in the // chain of recursion, thereby allowing `Item` to include itself in its own definition. // // This is a table so that added fields, like for example a `hash`, can be easily added