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 in // the future. 2: store @generated_name("nested_store") table { 1: items vector<box<Item>>; }; }:optional; }; /// An enumeration of things that may go wrong when trying to write a value to our store. type WriteError = flexible enum { UNKNOWN = 0; INVALID_KEY = 1; INVALID_VALUE = 2; ALREADY_EXISTS = 3; }; /// A very basic key-value store. @discoverable open protocol Store { /// Writes an item to the store. flexible WriteItem(struct { attempt Item; }) -> () error WriteError; };
CML
Client
// 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. { include: [ "syslog/client.shard.cml" ], program: { runner: "elf", binary: "bin/client_bin", }, use: [ { protocol: "examples.keyvaluestore.supporttrees.Store" }, ], config: { write_items: { type: "vector", max_count: 16, element: { type: "string", max_size: 64, }, }, // A newline separated list nested entries. The first line should be the key // for the nested store, and each subsequent entry should be a pointer to a text file // containing the string value. The name of that text file (without the `.txt` suffix) will // serve as the entries key. write_nested: { type: "vector", max_count: 16, element: { type: "string", max_size: 64, }, }, // A list of keys, all of which will be populated as null entries. write_null: { type: "vector", max_count: 16, element: { type: "string", max_size: 64, }, }, }, }
Server
// 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. { include: [ "syslog/client.shard.cml" ], program: { runner: "elf", binary: "bin/server_bin", }, capabilities: [ { protocol: "examples.keyvaluestore.supporttrees.Store" }, ], expose: [ { protocol: "examples.keyvaluestore.supporttrees.Store", from: "self", }, ], }
Realm
// 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. { children: [ { name: "client", url: "#meta/client.cm", }, { name: "server", url: "#meta/server.cm", }, ], offer: [ // Route the protocol under test from the server to the client. { protocol: "examples.keyvaluestore.supporttrees.Store", from: "#server", to: "#client", }, // Route diagnostics support to all children. { protocol: [ "fuchsia.inspect.InspectSink", "fuchsia.logger.LogSink", ], from: "parent", to: [ "#client", "#server", ], }, ], }
Client and server implementations can then be written in any supported language:
Rust
Client
// 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. use { anyhow::{Context as _, Error}, config::Config, fidl_examples_keyvaluestore_supporttrees::{Item, NestedStore, StoreMarker, Value}, fuchsia_component::client::connect_to_protocol, std::{thread, time}, }; #[fuchsia::main] async fn main() -> Result<(), Error> { println!("Started"); // Load the structured config values passed to this component at startup. let config = Config::take_from_startup_handle(); // Use the Component Framework runtime to connect to the newly spun up server component. We wrap // our retained client end in a proxy object that lets us asynchronously send `Store` requests // across the channel. let store = connect_to_protocol::<StoreMarker>()?; println!("Outgoing connection enabled"); // This client's structured config has one parameter, a vector of strings. Each string is the // path to a resource file whose filename is a key and whose contents are a value. We iterate // over them and try to write each key-value pair to the remote store. for key in config.write_items.into_iter() { let path = format!("/pkg/data/{}.txt", key); let value = std::fs::read_to_string(path.clone()) .with_context(|| format!("Failed to load {path}"))?; let res = store .write_item(&Item { key: key.clone(), value: Some(Box::new(Value::Bytes(value.into_bytes()))), }) .await; match res? { Ok(_) => println!("WriteItem Success at key: {}", key), Err(err) => println!("WriteItem Error: {}", err.into_primitive()), } } // Add nested entries to the key-value store as well. The entries are strings, where the first // line is the key of the entry, and each subsequent entry should be a pointer to a text file // containing the string value. The name of that text file (without the `.txt` suffix) will // serve as the entries key. for spec in config.write_nested.into_iter() { let mut items = vec![]; let mut nested_store = NestedStore::default(); let mut lines = spec.split("\n"); let key = lines.next().unwrap(); // For each entry, make a new entry in the `NestedStore` being built. for entry in lines { let path = format!("/pkg/data/{}.txt", entry); let contents = std::fs::read_to_string(path.clone()) .with_context(|| format!("Failed to load {path}"))?; items.push(Some(Box::new(Item { key: entry.to_string(), value: Some(Box::new(Value::Bytes(contents.into()))), }))); } nested_store.items = Some(items); // Send the `NestedStore`, represented as a vector of values. let res = store .write_item(&Item { key: key.to_string(), value: Some(Box::new(Value::Store(nested_store))), }) .await; match res? { Ok(_) => println!("WriteItem Success at key: {}", key), Err(err) => println!("WriteItem Error: {}", err.into_primitive()), } } // Each entry in this list is a null value in the store. for key in config.write_null.into_iter() { match store.write_item(&Item { key: key.to_string(), value: None }).await? { Ok(_) => println!("WriteItem Success at key: {}", key), Err(err) => println!("WriteItem Error: {}", err.into_primitive()), } } // TODO(https://fxbug.dev/42156498): We need to sleep here to make sure all logs get drained. Once the // referenced bug has been resolved, we can remove the sleep. thread::sleep(time::Duration::from_secs(2)); Ok(()) }
Server
// 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. // Note: For the clarity of this example, allow code to be unused. #![allow(dead_code)] use { anyhow::{Context as _, Error}, fidl_examples_keyvaluestore_supporttrees::{ Item, StoreRequest, StoreRequestStream, Value, WriteError, }, fuchsia_component::server::ServiceFs, futures::prelude::*, lazy_static::lazy_static, regex::Regex, std::cell::RefCell, std::collections::hash_map::Entry, std::collections::HashMap, std::str::from_utf8, }; lazy_static! { static ref KEY_VALIDATION_REGEX: Regex = Regex::new(r"^[A-Za-z]\w+[A-Za-z0-9]$").expect("Key validation regex failed to compile"); } // A representation of a key-value store that can contain an arbitrarily deep nesting of other // key-value stores. enum StoreNode { Leaf(Option<Vec<u8>>), Branch(Box<HashMap<String, StoreNode>>), } /// Recursive item writer, which takes a `StoreNode` that may not necessarily be the root node, and /// writes an entry to it. fn write_item( store: &mut HashMap<String, StoreNode>, attempt: Item, path: &str, ) -> Result<(), WriteError> { // Validate the key. if !KEY_VALIDATION_REGEX.is_match(attempt.key.as_str()) { println!("Write error: INVALID_KEY, For key: {}", attempt.key); return Err(WriteError::InvalidKey); } // Write to the store, validating that the key did not already exist. match store.entry(attempt.key) { Entry::Occupied(entry) => { println!("Write error: ALREADY_EXISTS, For key: {}", entry.key()); Err(WriteError::AlreadyExists) } Entry::Vacant(entry) => { let key = format!("{}{}", &path, entry.key()); match attempt.value { // Null entries are allowed. None => { println!("Wrote value: NONE at key: {}", key); entry.insert(StoreNode::Leaf(None)); } Some(value) => match *value { // If this is a nested store, recursively make a new store to insert at this // position. Value::Store(entry_list) => { // Validate the value - absent stores, items lists with no children, or any // of the elements within that list being empty boxes, are all not allowed. if entry_list.items.is_some() { let items = entry_list.items.unwrap(); if !items.is_empty() && items.iter().all(|i| i.is_some()) { let nested_path = format!("{}/", key); let mut nested_store = HashMap::<String, StoreNode>::new(); for item in items.into_iter() { write_item(&mut nested_store, *item.unwrap(), &nested_path)?; } println!("Created branch at key: {}", key); entry.insert(StoreNode::Branch(Box::new(nested_store))); return Ok(()); } } println!("Write error: INVALID_VALUE, For key: {}", key); return Err(WriteError::InvalidValue); } // This is a simple leaf node on this branch. Value::Bytes(value) => { // Validate the value. if value.is_empty() { println!("Write error: INVALID_VALUE, For key: {}", key); return Err(WriteError::InvalidValue); } println!("Wrote key: {}, value: {:?}", key, from_utf8(&value).unwrap()); entry.insert(StoreNode::Leaf(Some(value))); } }, } Ok(()) } } } /// Creates a new instance of the server. Each server has its own bespoke, per-connection instance /// of the key-value store. async fn run_server(stream: StoreRequestStream) -> Result<(), Error> { // Create a new in-memory key-value store. The store will live for the lifetime of the // connection between the server and this particular client. let store = RefCell::new(HashMap::<String, StoreNode>::new()); // Serve all requests on the protocol sequentially - a new request is not handled until its // predecessor has been processed. stream .map(|result| result.context("failed request")) .try_for_each(|request| async { // Match based on the method being invoked. match request { StoreRequest::WriteItem { attempt, responder } => { println!("WriteItem request received"); // The `responder` parameter is a special struct that manages the outgoing reply // to this method call. Calling `send` on the responder exactly once will send // the reply. responder .send(write_item(&mut store.borrow_mut(), attempt, "")) .context("error sending reply")?; println!("WriteItem response sent"); } StoreRequest::_UnknownMethod { ordinal, .. } => { println!("Received an unknown method with ordinal {ordinal}"); } } Ok(()) }) .await } // A helper enum that allows us to treat a `Store` service instance as a value. enum IncomingService { Store(StoreRequestStream), } #[fuchsia::main] async fn main() -> Result<(), Error> { println!("Started"); // Add a discoverable instance of our `Store` protocol - this will allow the client to see the // server and connect to it. let mut fs = ServiceFs::new_local(); fs.dir("svc").add_fidl_service(IncomingService::Store); fs.take_and_serve_directory_handle()?; println!("Listening for incoming connections"); // The maximum number of concurrent clients that may be served by this process. const MAX_CONCURRENT: usize = 10; // Serve each connection simultaneously, up to the `MAX_CONCURRENT` limit. fs.for_each_concurrent(MAX_CONCURRENT, |IncomingService::Store(stream)| { run_server(stream).unwrap_or_else(|e| println!("{:?}", e)) }) .await; Ok(()) }
C++ (Natural)
Client
// TODO(https://fxbug.dev/42060656): C++ (Natural) implementation.
Server
// TODO(https://fxbug.dev/42060656): C++ (Natural) implementation.
C++ (Wire)
Client
// TODO(https://fxbug.dev/42060656): C++ (Wire) implementation.
Server
// TODO(https://fxbug.dev/42060656): C++ (Wire) implementation.
HLCPP
Client
// TODO(https://fxbug.dev/42060656): HLCPP implementation.
Server
// TODO(https://fxbug.dev/42060656): HLCPP implementation.
fi-0157: Client/server end constraint must be a protocol
The first constraint applied to either client_end
or server_end
must point
to a protocol
definition:
library test.bad.fi0157;
type MyStruct = struct {};
alias ServerEnd = server_end:MyStruct;
Change the constraint to point to a protocol instead:
library test.good.fi0157;
protocol MyProtocol {};
alias ServerEnd = server_end:MyProtocol;
fi-0158: Cannot bound twice
An alias
declaration cannot change the value of an already set constraint on
the type it is aliasing:
library test.bad.fi0158;
alias ByteVec256 = vector<uint8>:256;
alias ByteVec512 = ByteVec256:512;
Instead, the unbounded definition should receive its own alias
declaration,
and each further constrained alias should inherit from it in turn:
library test.good.fi0158;
alias AliasOfVectorOfString = vector<string>;
alias AliasOfVectorOfStringSmall = AliasOfVectorOfString:8;
alias AliasOfVectorOfStringLarge = AliasOfVectorOfString:16;
This is disallowed to avoid confusion and compiler implementation complexity.
fi-0159: Struct cannot be optional
Structs cannot have the optional
constraint:
library test.bad.fi0159;
type Date = struct {
year uint16;
month uint8;
day uint8;
};
type Person = struct {
name string;
birthday Date:optional;
};
Change T:optional
to box<T>
to fix the issue:
library test.good.fi0159;
type Date = struct {
year uint16;
month uint8;
day uint8;
};
type Person = struct {
name string;
birthday box<Date>;
};
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 for more information.
fi-0160: Type cannot be marked as optional twice
This error occurs when a type is made optional twice. Usually, this occurs when the type is marked optional at both its use and declaration sites.
library test.bad.fi0160;
alias MyAlias = vector<string>:optional;
type MyStruct = struct {
my_member MyAlias:optional;
};
To fix this error, only make the type optional once.
For example, you can remove :optional
from the use site.
library test.good.fi0160a;
alias MyAlias = vector<string>:optional;
type MyStruct = struct {
my_member MyAlias;
};
You could also remove :optional
from the alias declaration.
library test.good.fi0160b;
alias MyAlias = vector<string>;
type MyStruct = struct {
my_member MyAlias:optional;
};
fi-0161: Must have non zero size
This error occurs when you try to set an array size constraint to 0. Arrays cannot be zero-sized.
library test.bad.fi0161;
type Person = struct {
name string;
nicknames array<string, 0>;
};
To fix this error, change the size constraint to a positive integer.
library test.good.fi0161;
type Person = struct {
name string;
nicknames array<string, 5>;
};
fi-0162: Wrong number of layout parameters
Certain FIDL layouts, like vector
and array
, take parameters. This error
indicates that the highlighted type has an incorrect number of parameters
specified:
library test.bad.fi0162a;
type Foo = struct {
bar array<8>;
};
It can also appear in cases where a non-parameterizable type has erroneously had parameters attached to it:
library test.bad.fi0162b;
type Foo = struct {
bar uint8<8>;
};
The fix is always to specify the correct number of parameters for the layout in question:
library test.good.fi0162;
type Foo = struct {
bar array<uint8, 8>;
};
The only parameterized types in FIDL are array<T, N>
, box<T>
, and
vector<T>
. The client_end
and server_end
types used to be parameterized in
an older version of the FIDL syntax, but this is no longer the case,
though it is a frequent source of run-ins with this error. These two types now
take their protocol specification as a (required) constraint instead.
Parameters, which are always listed inside of angle brackets <...>
, have some
similarities to constraints, which appear after the :...
character at the end
of the type. For example, at first blush, it may appear odd that array<T, N>
specifies its size as a parameter, while vector<T>:N
specifies its size as a
constraint. The difference is that parameters always affect the wire layout
shape of the type in question, while constraints merely alter the set of values
that are considered acceptable at encode/decode time for that type, with no
effect on wire layout.
See RFC-0050: FIDL Syntax Revamp for a more thorough discussion of the distinction between the two concepts.
fi-0163: Multiple constraint definitions
This error occurs when you try to define multiple constraint definitions using
more than one colon (:
). Multiple constraint definitions must use
the angled bracket syntax type:<constraint1, constraint2, etc>
.
library test.bad.fi0163;
type Person = struct {
name string;
favorite_color string:30:optional;
};
To fix this error, use the angle bracket syntax for constraints:
library test.good.fi0163;
type Person = struct {
name string;
favorite_color string:<30, optional>;
};
fi-0164: Too many constraints
This error occurs when you try to add more constraints to a type than are
supported. string
, for example, supports at most two constraints.
library test.bad.fi0164;
type Person = struct {
name string:<0, optional, 20>;
};
To fix this issue, remove the extra constraints:
library test.good.fi0164;
type Person = struct {
name string:<20, optional>;
};
fi-0165: Expected type
This error occurs when you use a constant or protocol identifier when FIDL is expecting a type.
library test.bad.fi0165;
type Person = struct {
name string;
nicknames vector<5>;
};
To fix this error, update your code to use a valid type:
library test.good.fi0165;
type Person = struct {
name string;
nicknames vector<string>:5;
};
Protocols are not considered FIDL types and also cannot be used where a type is expected.
fi-0166: Unexpected constraint
This error occurs when you try to use a constraint where it's not expected.
Typically, this is due to a named const
in the wrong position.
library test.bad.fi0166;
const MIN_SIZE uint8 = 1;
const MAX_SIZE uint8 = 5;
type Person = struct {
name string;
nicknames vector<string>:<MIN_SIZE, MAX_SIZE>;
};
To fix this error, remove the constraint:
library test.good.fi0166;
const MAX_SIZE uint8 = 5;
type Person = struct {
name string;
nicknames vector<string>:<MAX_SIZE>;
};
fi-0167: Cannot constrain twice
Re-assigning the transport bound for a client_end
or server_end
that has
already had a transport bound defined through an alias
declaration is
prohibited:
library test.bad.fi0167;
protocol MyOtherProtocol {};
alias ClientEnd = client_end:MyProtocol;
alias ServerEnd = server_end:MyProtocol;
protocol MyProtocol {
MyMethod(resource struct {
my_client ClientEnd:MyOtherProtocol;
}) -> (resource struct {
my_server ServerEnd:MyOtherProtocol;
});
};
Instead, aliasing of client_end
and server_end
types should be avoided
entirely:
library test.good.fi0167;
protocol MyProtocol {
MyMethod(resource struct {
my_client client_end:MyProtocol;
}) -> (resource struct {
my_server server_end:MyProtocol;
});
};
This is disallowed to avoid confusion and compiler implementation complexity.
fi-0168: Client/server end must have protocol constraint
The first constraint applied to either client_end
or server_end
must point
to a protocol
definition:
library test.bad.fi0168;
protocol MyProtocol {
MyMethod(resource struct {
server server_end;
});
};
Add a constraint pointing to the desired protocol:
library test.good.fi0168;
protocol MyProtocol {
MyMethod(resource struct {
server server_end:MyProtocol;
});
};
fi-0169: Boxed type cannot be optional
A type of the form box<T>
cannot have the optional
constraint applied to it:
library test.bad.fi0169;
type Color = struct {
red byte;
green byte;
blue byte;
};
type MyStruct = struct {
maybe_color box<Color>:optional;
};
Boxed types are optional by definition, so adding the extra constraint is unnecessary and redundant:
library test.good.fi0169;
type Color = struct {
red byte;
green byte;
blue byte;
};
type MyStruct = struct {
maybe_color box<Color>;
};
fi-0170
fi-0171: Boxed type should use optional constraint instead
Only a type using the struct
layout can be boxed; union
, vector
, string
,
client_end
, server_end
, and zx.Handle
must use the optional
constraint
instead:
library test.bad.fi0171;
using zx;
type MyStruct = resource struct {
my_resource_member box<zx.Handle>;
};
Convert box<T>
to T:optional
to fix this issue:
library test.good.fi0171;
using zx;
type MyStruct = resource struct {
my_resource_member zx.Handle:optional;
};
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 in // the future. 2: store @generated_name("nested_store") table { 1: items vector<box<Item>>; }; }:optional; }; /// An enumeration of things that may go wrong when trying to write a value to our store. type WriteError = flexible enum { UNKNOWN = 0; INVALID_KEY = 1; INVALID_VALUE = 2; ALREADY_EXISTS = 3; }; /// A very basic key-value store. @discoverable open protocol Store { /// Writes an item to the store. flexible WriteItem(struct { attempt Item; }) -> () error WriteError; };
CML
Client
// 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. { include: [ "syslog/client.shard.cml" ], program: { runner: "elf", binary: "bin/client_bin", }, use: [ { protocol: "examples.keyvaluestore.supporttrees.Store" }, ], config: { write_items: { type: "vector", max_count: 16, element: { type: "string", max_size: 64, }, }, // A newline separated list nested entries. The first line should be the key // for the nested store, and each subsequent entry should be a pointer to a text file // containing the string value. The name of that text file (without the `.txt` suffix) will // serve as the entries key. write_nested: { type: "vector", max_count: 16, element: { type: "string", max_size: 64, }, }, // A list of keys, all of which will be populated as null entries. write_null: { type: "vector", max_count: 16, element: { type: "string", max_size: 64, }, }, }, }
Server
// 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. { include: [ "syslog/client.shard.cml" ], program: { runner: "elf", binary: "bin/server_bin", }, capabilities: [ { protocol: "examples.keyvaluestore.supporttrees.Store" }, ], expose: [ { protocol: "examples.keyvaluestore.supporttrees.Store", from: "self", }, ], }
Realm
// 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. { children: [ { name: "client", url: "#meta/client.cm", }, { name: "server", url: "#meta/server.cm", }, ], offer: [ // Route the protocol under test from the server to the client. { protocol: "examples.keyvaluestore.supporttrees.Store", from: "#server", to: "#client", }, // Route diagnostics support to all children. { protocol: [ "fuchsia.inspect.InspectSink", "fuchsia.logger.LogSink", ], from: "parent", to: [ "#client", "#server", ], }, ], }
Client and server implementations can then be written in any supported language:
Rust
Client
// 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. use { anyhow::{Context as _, Error}, config::Config, fidl_examples_keyvaluestore_supporttrees::{Item, NestedStore, StoreMarker, Value}, fuchsia_component::client::connect_to_protocol, std::{thread, time}, }; #[fuchsia::main] async fn main() -> Result<(), Error> { println!("Started"); // Load the structured config values passed to this component at startup. let config = Config::take_from_startup_handle(); // Use the Component Framework runtime to connect to the newly spun up server component. We wrap // our retained client end in a proxy object that lets us asynchronously send `Store` requests // across the channel. let store = connect_to_protocol::<StoreMarker>()?; println!("Outgoing connection enabled"); // This client's structured config has one parameter, a vector of strings. Each string is the // path to a resource file whose filename is a key and whose contents are a value. We iterate // over them and try to write each key-value pair to the remote store. for key in config.write_items.into_iter() { let path = format!("/pkg/data/{}.txt", key); let value = std::fs::read_to_string(path.clone()) .with_context(|| format!("Failed to load {path}"))?; let res = store .write_item(&Item { key: key.clone(), value: Some(Box::new(Value::Bytes(value.into_bytes()))), }) .await; match res? { Ok(_) => println!("WriteItem Success at key: {}", key), Err(err) => println!("WriteItem Error: {}", err.into_primitive()), } } // Add nested entries to the key-value store as well. The entries are strings, where the first // line is the key of the entry, and each subsequent entry should be a pointer to a text file // containing the string value. The name of that text file (without the `.txt` suffix) will // serve as the entries key. for spec in config.write_nested.into_iter() { let mut items = vec![]; let mut nested_store = NestedStore::default(); let mut lines = spec.split("\n"); let key = lines.next().unwrap(); // For each entry, make a new entry in the `NestedStore` being built. for entry in lines { let path = format!("/pkg/data/{}.txt", entry); let contents = std::fs::read_to_string(path.clone()) .with_context(|| format!("Failed to load {path}"))?; items.push(Some(Box::new(Item { key: entry.to_string(), value: Some(Box::new(Value::Bytes(contents.into()))), }))); } nested_store.items = Some(items); // Send the `NestedStore`, represented as a vector of values. let res = store .write_item(&Item { key: key.to_string(), value: Some(Box::new(Value::Store(nested_store))), }) .await; match res? { Ok(_) => println!("WriteItem Success at key: {}", key), Err(err) => println!("WriteItem Error: {}", err.into_primitive()), } } // Each entry in this list is a null value in the store. for key in config.write_null.into_iter() { match store.write_item(&Item { key: key.to_string(), value: None }).await? { Ok(_) => println!("WriteItem Success at key: {}", key), Err(err) => println!("WriteItem Error: {}", err.into_primitive()), } } // TODO(https://fxbug.dev/42156498): We need to sleep here to make sure all logs get drained. Once the // referenced bug has been resolved, we can remove the sleep. thread::sleep(time::Duration::from_secs(2)); Ok(()) }
Server
// 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. // Note: For the clarity of this example, allow code to be unused. #![allow(dead_code)] use { anyhow::{Context as _, Error}, fidl_examples_keyvaluestore_supporttrees::{ Item, StoreRequest, StoreRequestStream, Value, WriteError, }, fuchsia_component::server::ServiceFs, futures::prelude::*, lazy_static::lazy_static, regex::Regex, std::cell::RefCell, std::collections::hash_map::Entry, std::collections::HashMap, std::str::from_utf8, }; lazy_static! { static ref KEY_VALIDATION_REGEX: Regex = Regex::new(r"^[A-Za-z]\w+[A-Za-z0-9]$").expect("Key validation regex failed to compile"); } // A representation of a key-value store that can contain an arbitrarily deep nesting of other // key-value stores. enum StoreNode { Leaf(Option<Vec<u8>>), Branch(Box<HashMap<String, StoreNode>>), } /// Recursive item writer, which takes a `StoreNode` that may not necessarily be the root node, and /// writes an entry to it. fn write_item( store: &mut HashMap<String, StoreNode>, attempt: Item, path: &str, ) -> Result<(), WriteError> { // Validate the key. if !KEY_VALIDATION_REGEX.is_match(attempt.key.as_str()) { println!("Write error: INVALID_KEY, For key: {}", attempt.key); return Err(WriteError::InvalidKey); } // Write to the store, validating that the key did not already exist. match store.entry(attempt.key) { Entry::Occupied(entry) => { println!("Write error: ALREADY_EXISTS, For key: {}", entry.key()); Err(WriteError::AlreadyExists) } Entry::Vacant(entry) => { let key = format!("{}{}", &path, entry.key()); match attempt.value { // Null entries are allowed. None => { println!("Wrote value: NONE at key: {}", key); entry.insert(StoreNode::Leaf(None)); } Some(value) => match *value { // If this is a nested store, recursively make a new store to insert at this // position. Value::Store(entry_list) => { // Validate the value - absent stores, items lists with no children, or any // of the elements within that list being empty boxes, are all not allowed. if entry_list.items.is_some() { let items = entry_list.items.unwrap(); if !items.is_empty() && items.iter().all(|i| i.is_some()) { let nested_path = format!("{}/", key); let mut nested_store = HashMap::<String, StoreNode>::new(); for item in items.into_iter() { write_item(&mut nested_store, *item.unwrap(), &nested_path)?; } println!("Created branch at key: {}", key); entry.insert(StoreNode::Branch(Box::new(nested_store))); return Ok(()); } } println!("Write error: INVALID_VALUE, For key: {}", key); return Err(WriteError::InvalidValue); } // This is a simple leaf node on this branch. Value::Bytes(value) => { // Validate the value. if value.is_empty() { println!("Write error: INVALID_VALUE, For key: {}", key); return Err(WriteError::InvalidValue); } println!("Wrote key: {}, value: {:?}", key, from_utf8(&value).unwrap()); entry.insert(StoreNode::Leaf(Some(value))); } }, } Ok(()) } } } /// Creates a new instance of the server. Each server has its own bespoke, per-connection instance /// of the key-value store. async fn run_server(stream: StoreRequestStream) -> Result<(), Error> { // Create a new in-memory key-value store. The store will live for the lifetime of the // connection between the server and this particular client. let store = RefCell::new(HashMap::<String, StoreNode>::new()); // Serve all requests on the protocol sequentially - a new request is not handled until its // predecessor has been processed. stream .map(|result| result.context("failed request")) .try_for_each(|request| async { // Match based on the method being invoked. match request { StoreRequest::WriteItem { attempt, responder } => { println!("WriteItem request received"); // The `responder` parameter is a special struct that manages the outgoing reply // to this method call. Calling `send` on the responder exactly once will send // the reply. responder .send(write_item(&mut store.borrow_mut(), attempt, "")) .context("error sending reply")?; println!("WriteItem response sent"); } StoreRequest::_UnknownMethod { ordinal, .. } => { println!("Received an unknown method with ordinal {ordinal}"); } } Ok(()) }) .await } // A helper enum that allows us to treat a `Store` service instance as a value. enum IncomingService { Store(StoreRequestStream), } #[fuchsia::main] async fn main() -> Result<(), Error> { println!("Started"); // Add a discoverable instance of our `Store` protocol - this will allow the client to see the // server and connect to it. let mut fs = ServiceFs::new_local(); fs.dir("svc").add_fidl_service(IncomingService::Store); fs.take_and_serve_directory_handle()?; println!("Listening for incoming connections"); // The maximum number of concurrent clients that may be served by this process. const MAX_CONCURRENT: usize = 10; // Serve each connection simultaneously, up to the `MAX_CONCURRENT` limit. fs.for_each_concurrent(MAX_CONCURRENT, |IncomingService::Store(stream)| { run_server(stream).unwrap_or_else(|e| println!("{:?}", e)) }) .await; Ok(()) }
C++ (Natural)
Client
// TODO(https://fxbug.dev/42060656): C++ (Natural) implementation.
Server
// TODO(https://fxbug.dev/42060656): C++ (Natural) implementation.
C++ (Wire)
Client
// TODO(https://fxbug.dev/42060656): C++ (Wire) implementation.
Server
// TODO(https://fxbug.dev/42060656): C++ (Wire) implementation.
HLCPP
Client
// TODO(https://fxbug.dev/42060656): HLCPP implementation.
Server
// TODO(https://fxbug.dev/42060656): HLCPP implementation.
fi-0172: Resource definition must use uint32 subtype
The subtype of a resource_definition
declaration must be uint32
:
library test.bad.fi0172;
type MySubtype = strict enum : uint32 {
NONE = 0;
};
resource_definition MyResource : uint8 {
properties {
subtype MySubtype;
};
};
Change the subtype to an uint32
to fix this error:
library test.good.fi0172;
type MySubtype = strict enum : uint32 {
NONE = 0;
};
resource_definition MyResource : uint32 {
properties {
subtype MySubtype;
};
};
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-0173: Resource definition must specify subtype
The resource_definition
declaration cannot omit the subtype
member:
library test.bad.fi0173;
resource_definition MyResource : uint32 {
properties {
rights uint32;
};
};
Point this member to a valid enum : uint32
declaration:
library test.good.fi0173;
resource_definition MyResource : uint32 {
properties {
subtype flexible enum : uint32 {};
rights uint32;
};
};
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-0174
fi-0175: Resource definition subtype property must refer to enum
The resource_definition
declaration cannot use a non-enum
as the subtype
member:
library test.bad.fi0175;
resource_definition MyResource : uint32 {
properties {
subtype struct {};
};
};
Point this member to a valid enum : uint32
declaration:
library test.good.fi0175;
resource_definition MyResource : uint32 {
properties {
subtype flexible enum : uint32 {};
};
};
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-0176
fi-0177: Resource definition rights property must refer to bits
The resource_definition
declaration cannot use a non-bits
as the rights
member:
library test.bad.fi0177;
type MySubtype = enum : uint32 {
NONE = 0;
VMO = 3;
};
resource_definition MyResource : uint32 {
properties {
subtype MySubtype;
rights string;
};
};
Point this member to a valid bits : uint32
declaration:
library test.good.fi0177;
type MySubtype = enum : uint32 {
NONE = 0;
VMO = 3;
};
resource_definition MyResource : uint32 {
properties {
subtype MySubtype;
rights uint32;
};
};
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-0178: Unused import
Not referencing a dependency imported via the using
declaration is an error:
library test.bad.fi0178;
using dependent;
type Foo = struct {
does_not int64;
use_dependent int32;
};
Make sure all such imports are used in the library importing, either by actually referencing the import, or removing the unused dependency:
library test.good.fi0178;
using dependent;
type Foo = struct {
dep dependent.Bar;
};
fi-0179: Newtypes cannot be constrained
Newtypes from RFC-0052: Type aliasing and new types are not
allowed to be constrained. For example, a newtype of string
cannot be
constrained with :optional
:
library test.bad.fi0179;
type Name = string;
type Info = struct {
name Name:optional;
};
In this situation, we can make make the name
field optional by putting it in a
table rather than a struct:
library test.good.fi0179;
type Name = string;
type Info = table {
1: name Name;
};
This restriction simplifies the design of newtypes. It's not clear what the API and ABI should look like for a constrained newtype in general (e.g., should the constraints apply to the newtype itself, or flow through to the underlying type?).
fi-0180: Zircon C types are experimental
The built-in types usize
, uintptr
, uchar
, and experimental_pointer
are
being developed for the Zither project. They cannot be used in ordinary FIDL
libraries:
library test.bad.fi0180;
type Data = struct {
size usize64;
};
Instead, use a different type, for example uint64
instead of usize
:
library test.good.fi0180;
type Data = struct {
size uint64;
};
fi-0181: Library attribute argument references constant
Attribute arguments on library declarations are not allowed to reference constants:
@custom_attribute(VALUE)
library test.bad.fi0181a;
const VALUE string = "hello";
Instead, provide a literal argument:
@custom_attribute("hello")
library test.good.fi0181a;
This restriction exists because it is rarely needed and supporting it adds unwarranted complexity to the compiler.
fi-0182
fi-0183
fi-0184: Unexpected control character
String literals are not allowed to contain raw control characters (ASCII
characters from 0x00
to 0x1f
):
library test.bad.fi0184;
const TAB string = " "; // literal tab character
Instead, use an escape sequence. In this case, \t
is the right one:
library test.good.fi0184a;
const TAB string = "\t";
Alternatively, you can use a Unicode escape sequence. This works for any Unicode code point:
library test.good.fi0184b;
const TAB string = "\u{9}";
Raw control characters are not allowed in string literals because they are all either whitespace or non-printable, so they would be confusing and hard to notice if embedded directly in the FIDL source file.
fi-0185: Unicode escape sequence missing braces
Unicode escape sequences in string literals must specify a code point in braces:
library test.bad.fi0185;
const SMILE string = "\u";
To fix the error, specify a code point in braces:
library test.good.fi0185;
const SMILE string = "\u{1F600}";
fi-0186: Unterminated Unicode escape sequence
Unicode escape sequences in string literals must be terminated:
library test.bad.fi0186;
const SMILE string = "\u{1F600";
To terminate the escape sequence, add a closing brace }
:
library test.good.fi0186;
const SMILE string = "\u{1F600}";
fi-0187: Empty Unicode escape sequence
Unicode escape sequences in string literals must have at least one hex digit:
library test.bad.fi0187;
const SMILE string = "\u{}";
To fix the error, add hex digits to specify a Unicode code point:
library test.good.fi0187;
const SMILE string = "\u{1F600}";
fi-0188: Too many digits in Unicode escape sequence
Unicode escape sequences in string literals cannot have more than 6 hex digits:
library test.bad.fi0188;
const SMILE string = "\u{001F600}";
To fix the error, specify at most 6 hex digits. In this case, there are leading zeros that we can remove:
library test.good.fi0188;
const SMILE string = "\u{1F600}";
This restriction exists because all valid Unicode code points fit in 6 hex digits, so there is no reason to allow more digits than that.
fi-0189: Unicode code point too large
Unicode escape sequences in string literals cannot specify a Unicode code point
greater than the maximum of 0x10ffff
:
library test.bad.fi0189;
const TOO_LARGE string = "\u{110000}";
Instead, make sure the code point is valid:
library test.good.fi0189;
const MAX_CODEPOINT string = "\u{10ffff}";
fi-0190
fi-0191: Method must specify strictness
This error indicates that a FIDL method does not have a strict
or flexible
modifier.
library test.bad.fi0191;
open protocol Example {
OneWay();
};
To fix this, add either strict
or flexible
to the method. If this is an
existing method, you must use strict
and should see the compatibility
guide for guidance on changing it to flexible
. If
this is a new method, you should see the API rubric for
guidance on which to choose.
library test.good.fi0191;
open protocol Example {
flexible OneWay();
};
FIDL is currently undergoing a migration in order to support handling unknown
interactions, defined in RFC-0138. This new feature allows the
modifiers strict
and flexible
to apply to FIDL methods and events.
Historically, all methods behaved as though they were strict
, however at the
end of this migration, the default value will be flexible
. To avoid confusion
and possible issues arising from changing the method default modifier from
strict
to flexible
, the method modifier is required during this transitional
period. When the migration is complete, this will be changed from an error to a
linter suggestion.
For more information about unknown interactions, see the FIDL language reference.
fi-0192: Protocol must specify openness
This error indicates that a FIDL protocol does not have an open
, ajar
, or
closed
modifier.
library test.bad.fi0192;
protocol ImplicitOpenness {};
To fix this, add open
, ajar
, or closed
to the protocol. If this is an
existing protocol, you must use closed
and should see the compatibility
guide for guidance on changing it to open
or
ajar
. If this is a new method, you should see the API
rubric for guidance on which to choose.
library test.good.fi0192;
open protocol ImplicitOpenness {};
FIDL is currently undergoing a migration in order to support handling unknown
interactions, defined in RFC-0138. This new feature adds three
new modifiers, open
, ajar
, and closed
, which apply to FIDL protocols.
Historically, all protocols behaved as though they were closed
, however at the
end of this migration, the default value will be open
. To avoid confusion and
possible issues arising from changing the protocol default modifier from
closed
to open
, the protocol modifier is required during this transitional
period. When the migration is complete, this will be changed from an error to a
linter suggestion.
For more information about unknown interactions, see the FIDL language reference.
fi-0193: Cannot box type
Types other than structs cannot be boxed. For example, a primitive type cannot be boxed:
library test.bad.fi0193;
type MyStruct = struct {
my_member box<bool>;
};
To box a primitive, put it in a single-member struct
instead:
library test.good.fi0193;
type MyStruct = struct {
my_member box<struct {
my_bool bool;
}>;
};
Note that some types can be made optional via the use of 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 in // the future. 2: store @generated_name("nested_store") table { 1: items vector<box<Item>>; }; }:optional; }; /// An enumeration of things that may go wrong when trying to write a value to our store. type WriteError = flexible enum { UNKNOWN = 0; INVALID_KEY = 1; INVALID_VALUE = 2; ALREADY_EXISTS = 3; }; /// A very basic key-value store. @discoverable open protocol Store { /// Writes an item to the store. flexible WriteItem(struct { attempt Item; }) -> () error WriteError; };
CML
Client
// 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. { include: [ "syslog/client.shard.cml" ], program: { runner: "elf", binary: "bin/client_bin", }, use: [ { protocol: "examples.keyvaluestore.supporttrees.Store" }, ], config: { write_items: { type: "vector", max_count: 16, element: { type: "string", max_size: 64, }, }, // A newline separated list nested entries. The first line should be the key // for the nested store, and each subsequent entry should be a pointer to a text file // containing the string value. The name of that text file (without the `.txt` suffix) will // serve as the entries key. write_nested: { type: "vector", max_count: 16, element: { type: "string", max_size: 64, }, }, // A list of keys, all of which will be populated as null entries. write_null: { type: "vector", max_count: 16, element: { type: "string", max_size: 64, }, }, }, }
Server
// 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. { include: [ "syslog/client.shard.cml" ], program: { runner: "elf", binary: "bin/server_bin", }, capabilities: [ { protocol: "examples.keyvaluestore.supporttrees.Store" }, ], expose: [ { protocol: "examples.keyvaluestore.supporttrees.Store", from: "self", }, ], }
Realm
// 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. { children: [ { name: "client", url: "#meta/client.cm", }, { name: "server", url: "#meta/server.cm", }, ], offer: [ // Route the protocol under test from the server to the client. { protocol: "examples.keyvaluestore.supporttrees.Store", from: "#server", to: "#client", }, // Route diagnostics support to all children. { protocol: [ "fuchsia.inspect.InspectSink", "fuchsia.logger.LogSink", ], from: "parent", to: [ "#client", "#server", ], }, ], }
Client and server implementations can then be written in any supported language:
Rust
Client
// 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. use { anyhow::{Context as _, Error}, config::Config, fidl_examples_keyvaluestore_supporttrees::{Item, NestedStore, StoreMarker, Value}, fuchsia_component::client::connect_to_protocol, std::{thread, time}, }; #[fuchsia::main] async fn main() -> Result<(), Error> { println!("Started"); // Load the structured config values passed to this component at startup. let config = Config::take_from_startup_handle(); // Use the Component Framework runtime to connect to the newly spun up server component. We wrap // our retained client end in a proxy object that lets us asynchronously send `Store` requests // across the channel. let store = connect_to_protocol::<StoreMarker>()?; println!("Outgoing connection enabled"); // This client's structured config has one parameter, a vector of strings. Each string is the // path to a resource file whose filename is a key and whose contents are a value. We iterate // over them and try to write each key-value pair to the remote store. for key in config.write_items.into_iter() { let path = format!("/pkg/data/{}.txt", key); let value = std::fs::read_to_string(path.clone()) .with_context(|| format!("Failed to load {path}"))?; let res = store .write_item(&Item { key: key.clone(), value: Some(Box::new(Value::Bytes(value.into_bytes()))), }) .await; match res? { Ok(_) => println!("WriteItem Success at key: {}", key), Err(err) => println!("WriteItem Error: {}", err.into_primitive()), } } // Add nested entries to the key-value store as well. The entries are strings, where the first // line is the key of the entry, and each subsequent entry should be a pointer to a text file // containing the string value. The name of that text file (without the `.txt` suffix) will // serve as the entries key. for spec in config.write_nested.into_iter() { let mut items = vec![]; let mut nested_store = NestedStore::default(); let mut lines = spec.split("\n"); let key = lines.next().unwrap(); // For each entry, make a new entry in the `NestedStore` being built. for entry in lines { let path = format!("/pkg/data/{}.txt", entry); let contents = std::fs::read_to_string(path.clone()) .with_context(|| format!("Failed to load {path}"))?; items.push(Some(Box::new(Item { key: entry.to_string(), value: Some(Box::new(Value::Bytes(contents.into()))), }))); } nested_store.items = Some(items); // Send the `NestedStore`, represented as a vector of values. let res = store .write_item(&Item { key: key.to_string(), value: Some(Box::new(Value::Store(nested_store))), }) .await; match res? { Ok(_) => println!("WriteItem Success at key: {}", key), Err(err) => println!("WriteItem Error: {}", err.into_primitive()), } } // Each entry in this list is a null value in the store. for key in config.write_null.into_iter() { match store.write_item(&Item { key: key.to_string(), value: None }).await? { Ok(_) => println!("WriteItem Success at key: {}", key), Err(err) => println!("WriteItem Error: {}", err.into_primitive()), } } // TODO(https://fxbug.dev/42156498): We need to sleep here to make sure all logs get drained. Once the // referenced bug has been resolved, we can remove the sleep. thread::sleep(time::Duration::from_secs(2)); Ok(()) }
Server
// 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. // Note: For the clarity of this example, allow code to be unused. #![allow(dead_code)] use { anyhow::{Context as _, Error}, fidl_examples_keyvaluestore_supporttrees::{ Item, StoreRequest, StoreRequestStream, Value, WriteError, }, fuchsia_component::server::ServiceFs, futures::prelude::*, lazy_static::lazy_static, regex::Regex, std::cell::RefCell, std::collections::hash_map::Entry, std::collections::HashMap, std::str::from_utf8, }; lazy_static! { static ref KEY_VALIDATION_REGEX: Regex = Regex::new(r"^[A-Za-z]\w+[A-Za-z0-9]$").expect("Key validation regex failed to compile"); } // A representation of a key-value store that can contain an arbitrarily deep nesting of other // key-value stores. enum StoreNode { Leaf(Option<Vec<u8>>), Branch(Box<HashMap<String, StoreNode>>), } /// Recursive item writer, which takes a `StoreNode` that may not necessarily be the root node, and /// writes an entry to it. fn write_item( store: &mut HashMap<String, StoreNode>, attempt: Item, path: &str, ) -> Result<(), WriteError> { // Validate the key. if !KEY_VALIDATION_REGEX.is_match(attempt.key.as_str()) { println!("Write error: INVALID_KEY, For key: {}", attempt.key); return Err(WriteError::InvalidKey); } // Write to the store, validating that the key did not already exist. match store.entry(attempt.key) { Entry::Occupied(entry) => { println!("Write error: ALREADY_EXISTS, For key: {}", entry.key()); Err(WriteError::AlreadyExists) } Entry::Vacant(entry) => { let key = format!("{}{}", &path, entry.key()); match attempt.value { // Null entries are allowed. None => { println!("Wrote value: NONE at key: {}", key); entry.insert(StoreNode::Leaf(None)); } Some(value) => match *value { // If this is a nested store, recursively make a new store to insert at this // position. Value::Store(entry_list) => { // Validate the value - absent stores, items lists with no children, or any // of the elements within that list being empty boxes, are all not allowed. if entry_list.items.is_some() { let items = entry_list.items.unwrap(); if !items.is_empty() && items.iter().all(|i| i.is_some()) { let nested_path = format!("{}/", key); let mut nested_store = HashMap::<String, StoreNode>::new(); for item in items.into_iter() { write_item(&mut nested_store, *item.unwrap(), &nested_path)?; } println!("Created branch at key: {}", key); entry.insert(StoreNode::Branch(Box::new(nested_store))); return Ok(()); } } println!("Write error: INVALID_VALUE, For key: {}", key); return Err(WriteError::InvalidValue); } // This is a simple leaf node on this branch. Value::Bytes(value) => { // Validate the value. if value.is_empty() { println!("Write error: INVALID_VALUE, For key: {}", key); return Err(WriteError::InvalidValue); } println!("Wrote key: {}, value: {:?}", key, from_utf8(&value).unwrap()); entry.insert(StoreNode::Leaf(Some(value))); } }, } Ok(()) } } } /// Creates a new instance of the server. Each server has its own bespoke, per-connection instance /// of the key-value store. async fn run_server(stream: StoreRequestStream) -> Result<(), Error> { // Create a new in-memory key-value store. The store will live for the lifetime of the // connection between the server and this particular client. let store = RefCell::new(HashMap::<String, StoreNode>::new()); // Serve all requests on the protocol sequentially - a new request is not handled until its // predecessor has been processed. stream .map(|result| result.context("failed request")) .try_for_each(|request| async { // Match based on the method being invoked. match request { StoreRequest::WriteItem { attempt, responder } => { println!("WriteItem request received"); // The `responder` parameter is a special struct that manages the outgoing reply // to this method call. Calling `send` on the responder exactly once will send // the reply. responder .send(write_item(&mut store.borrow_mut(), attempt, "")) .context("error sending reply")?; println!("WriteItem response sent"); } StoreRequest::_UnknownMethod { ordinal, .. } => { println!("Received an unknown method with ordinal {ordinal}"); } } Ok(()) }) .await } // A helper enum that allows us to treat a `Store` service instance as a value. enum IncomingService { Store(StoreRequestStream), } #[fuchsia::main] async fn main() -> Result<(), Error> { println!("Started"); // Add a discoverable instance of our `Store` protocol - this will allow the client to see the // server and connect to it. let mut fs = ServiceFs::new_local(); fs.dir("svc").add_fidl_service(IncomingService::Store); fs.take_and_serve_directory_handle()?; println!("Listening for incoming connections"); // The maximum number of concurrent clients that may be served by this process. const MAX_CONCURRENT: usize = 10; // Serve each connection simultaneously, up to the `MAX_CONCURRENT` limit. fs.for_each_concurrent(MAX_CONCURRENT, |IncomingService::Store(stream)| { run_server(stream).unwrap_or_else(|e| println!("{:?}", e)) }) .await; Ok(()) }
C++ (Natural)
Client
// TODO(https://fxbug.dev/42060656): C++ (Natural) implementation.
Server
// TODO(https://fxbug.dev/42060656): C++ (Natural) implementation.
C++ (Wire)
Client
// TODO(https://fxbug.dev/42060656): C++ (Wire) implementation.
Server
// TODO(https://fxbug.dev/42060656): C++ (Wire) implementation.
HLCPP
Client
// TODO(https://fxbug.dev/42060656): HLCPP implementation.
Server
// TODO(https://fxbug.dev/42060656): HLCPP implementation.
fi-0194
fi-0195
fi-0196
fi-0200
fi-0201: Platform version not selected
This error occurs when you compile a versioned FIDL library without choosing a version:
// fidlc --files test.fidl --out test.json
@available(platform="foo", added=1)
library test.bad.fi0201;
To fix it, choose a version with the --available
command line flag:
// fidlc --files test.fidl --out test.json --available foo:1
@available(platform="foo", added=1)
library test.good.fi0201;
The version must be a number greater than or equal to 1, or one of the special
versions NEXT
and HEAD
. For more information, see the documentation for
FIDL versioning.
fi-0202
fi-0203: Removed and replaced are mutually exclusive
The @available
attribute supports the arguments removed
and replaced
, but
they can't be used together:
@available(added=1)
library test.bad.fi0203;
protocol Foo {
@available(removed=2, replaced=2)
Foo();
};
To fix the error, delete one of the arguments. If you intend to remove the
element without replacing it, keep removed
and delete replaced
:
@available(added=1)
library test.good.fi0203a;
open protocol Foo {
@available(removed=2)
strict Foo();
};
Alternatively, if you are swapping the element with a new definition, keep
replaced
and delete removed
:
@available(added=1)
library test.good.fi0203b;
open protocol Foo {
@available(replaced=2)
strict Foo();
@available(added=2)
flexible Foo();
};
It doesn't make sense to use removed
and replaced
together because they have
opposite meanings. When an element is marked removed
, fidlc validates that
there IS NOT a replacement element added at the same version. When an
element is marked replaced
, fidlc validates that there IS a replacement
element added at the same version.
See FIDL versioning to learn more about versioning.
fi-0204: Library cannot be replaced
The @available
attribute's replaced
argument cannot be used on a library
declaration:
@available(added=1, replaced=2)
library test.bad.fi0204;
Instead, use the removed
argument:
@available(added=1, removed=2)
library test.good.fi0204;
The replaced
argument indicates that an element is replaced by a new
definition. This is not supported for an entire library, since we assume each
library only has one set of files that defines it.
See FIDL versioning to learn more about versioning.
fi-0205: Invalid @available(removed=N)
When an element is marked @available(removed=N)
, it means the element can no
longer be used at version N
. You cannot reuse its name:
@available(added=1)
library test.bad.fi0204;
open protocol Foo {
@available(removed=2)
strict Bar();
@available(added=2)
flexible Bar();
};
If you want to replace the element with a new definition (same API and ABI), use
the replaced
argument instead of the removed
argument:
@available(added=1)
library test.good.fi0204a;
open protocol Foo {
@available(replaced=2)
strict Bar();
@available(added=2)
flexible Bar();
};
If you want to remove the element and define a new, unrelated element (different API and ABI), choose a different name for the new element:
@available(added=1)
library test.good.fi0204b;
open protocol Foo {
@available(removed=2)
strict Bar();
@available(added=2)
flexible NewBar();
};
If you really want to reuse the name (same API, different ABI), use the
renamed
argument to rename the old element post-removal, freeing up its
original name:
@available(added=1)
library test.good.fi0204c;
open protocol Foo {
@available(removed=2, renamed="DeprecatedBar")
strict Bar();
@available(added=2)
@selector("NewBar")
flexible Bar();
};
Notice that in this case you must use @selector
to ensure the new method has a
different ABI.
See FIDL versioning to learn more about versioning.
fi-0206: Invalid @available(replaced=N)
When an element is marked @available(replaced=N)
, it means the element is
replaced by a new definition marked @available(added=N)
. The FIDL compiler
will report an error if it cannot find such a definition:
@available(added=1)
library test.bad.fi0205;
open protocol Foo {
@available(replaced=2)
strict Bar();
};
If you did not intend to replace the element, use the removed
argument instead
of the replaced
argument:
@available(added=1)
library test.good.fi0205a;
open protocol Foo {
@available(removed=2)
strict Bar();
};
If you intended to replace the element, add a replacement definition:
@available(added=1)
library test.good.fi0205b;
open protocol Foo {
@available(replaced=2)
strict Bar();
@available(added=2)
flexible Bar();
};
See FIDL versioning to learn more about versioning.
fi-0207: Type shape integer overflow
A FIDL type must not be so large that its size overflows uint32
:
library test.bad.fi0207;
type Foo = struct {
bytes array<uint64, 536870912>;
};
To fix the error, use a smaller array size:
library test.good.fi0207;
type Foo = struct {
bytes array<uint64, 100>;
};
In practice FIDL types should be far smaller than 232 bytes, because they are usually sent over Zircon channels, which are limited to 64 KiB per message.
fi-0208: Reserved platform
Certain platform names are reserved by FIDL. For example, the "unversioned" platform is reserved to represent libraries that don't use versioning:
@available(platform="unversioned", added=1)
library test.bad.fi0208;
Choose a different platform name instead:
@available(platform="foo", added=1)
library test.good.fi0208;
See FIDL versioning to learn more about versioning.
fi-0209: Reserved fields are not allowed
FIDL no longer supports reserved
table or union fields:
library test.bad.fi0209;
type User = table {
1: reserved;
2: email string;
};
The main purpose of reserved fields was to avoid accidental reuse of ordinals.
With FIDL versioning, that is no
longer an issue. You can annotate old fields @available(removed=N)
while
keeping them (and their ordinals) in the source file:
@available(added=1)
library test.good.fi0209a;
type User = table {
@available(removed=2)
1: name string;
2: email string;
};
Another use of reserved
was to document the intended future use of an ordinal.
In that case, consider defining the field at the unstable API level HEAD
:
@available(added=1)
library test.good.fi0209b;
type User = table {
@available(added=HEAD)
1: name string;
2: email string;
};
For any other uses of reserved
, consider leaving a comment instead:
library test.good.fi0209c;
type User = table {
// We skip ordinal 1 because...
2: email string;
};
fi-0210: Invalid location in @discoverable client or server argument
The allowed client
or server
location for a discoverable protocol must be a
(possibly empty) comma separated list of platform
and external
. In other words one of:
- ""
- "platform"
- "external"
- "platform,external"
- "external,platform"
library test.bad.fi0210;
@discoverable(server="platform,canada")
protocol Example {};
Make sure these arguments are correct:
library test.good.fi0210;
@discoverable(server="platform")
protocol Example {};
fi-0211: Element cannot be renamed
The @available
attribute's renamed
argument can only be used on members of
declarations, not on declarations themselves:
@available(added=1)
library test.bad.fi0211;
@available(replaced=2, renamed="Bar")
type Foo = struct {};
@available(added=2)
type Bar = struct {};
Instead of renaming a declaration, remove the old one and add a new one:
@available(added=1)
library test.good.fi0211;
@available(removed=2)
type Foo = struct {};
@available(added=2)
type Bar = struct {};
Renaming is only supported on members because the FIDL compiler can compare their ABI identity (e.g. table ordinal) to ensure it is done correctly.
The renamed
argument is also not allowed on the following elements either:
library
: You can't rename a library from within because the FIDL toolchain assumes each library has a single name. Instead, you should create a new library with the desired name and migrate users to it.compose
: You can't rename a protocol composition because compositions are not named to begin with.
See FIDL versioning to learn more about versioning.
fi-0212: Renamed without replaced or removed
The @available
argument renamed
is not allowed on its own. It must be used
together with the replaced
or removed
argument:
@available(added=1)
library test.bad.fi0212;
protocol Foo {
@available(renamed="New")
Old();
};
If you just want to rename the element at version N
, use replaced=N
and
define a replacement with the new name marked added=N
:
@available(added=1)
library test.good.fi0212a;
protocol Foo {
@available(replaced=2, renamed="New")
Old();
@available(added=2)
@selector("Old")
New();
};
In this case, the replacement method must override @selector
for ABI
compatibility.
Alternatively, if you want to remove the element at version N
and refer to it
by a different name after its removal, use removed=N
:
@available(added=1)
library test.good.fi0212b;
protocol Foo {
@available(removed=2, renamed="DeprecatedOld")
Old();
};
In this case, the new name will only be used when targeting multiple versions
(e.g. --available test:1,2
) since that is the only way to include the element
while also targeting a version past its removal.
See FIDL versioning to learn more about versioning.
fi-0213: Renamed to same name
When renaming an element with the @available
argument renamed
, the new name
cannot be the same as the element's original name:
@available(added=1)
library test.bad.fi0213;
type Foo = table {
@available(replaced=2, renamed="bar")
1: bar string;
@available(added=2)
1: bar string:10;
};
To fix the error, remove the renamed
argument:
@available(added=1)
library test.good.fi0213a;
type Foo = table {
@available(replaced=2)
1: bar string;
@available(added=2)
1: bar string:10;
};
Alternatively, keep the renamed
argument but choose a different name:
@available(added=1)
library test.good.fi0213b;
type Foo = table {
@available(replaced=2, renamed="baz")
1: bar string;
@available(added=2)
1: baz string:10;
};
See FIDL versioning to learn more about versioning.
fi-0214: Invalid @available(removed=N, renamed="NewName")
This is like fi-0205: Invalid @available(removed=N)
, but for when
the renamed
argument is involved.
When an element is marked @available(removed=N, renamed="NewName")
, it means
the element can no longer be used at version N
, and is renamed to "NewName"
post-removal. You cannot use "NewName" for something else:
@available(added=1)
library test.bad.fi0214;
open protocol Foo {
@available(removed=2, renamed="NewName")
strict OldName();
@available(added=2)
flexible NewName();
};
If you want to rename the element while keeping its ABI, use the replaced
argument instead of the removed
argument:
@available(added=1)
library test.good.fi0214a;
open protocol Foo {
@available(replaced=2, renamed="NewName")
strict OldName();
@available(added=2)
@selector("OldName")
flexible NewName();
};
Notice that in this case you must use @selector
to ensure the renamed method
has the same ABI.
If you want the new element to have a different ABI, then keep removed
and
ensure that the renamed
argument and the new element use different names:
@available(added=1)
library test.good.fi0214b;
open protocol Foo {
@available(removed=2, renamed="NewName")
strict OldName();
@available(added=2)
strict DifferentName();
};
See FIDL versioning to learn more about versioning.
fi-0215: Invalid @available(replaced=N, renamed="NewName")
This is like fi-0206: Invalid @available(replaced=N)
, but for when
the renamed
argument is involved.
When an element is marked @available(replaced=N, renamed="NewName")
, it means
the element is replaced by a new definition named "NewName" and marked with
@available(added=N)
. The FIDL compiler will report an error if it cannot find
such a definition:
@available(added=1)
library test.bad.fi0215;
open protocol Foo {
@available(replaced=2, renamed="NewName")
strict OldName();
};
To fix the error, define an element with the new name:
@available(added=1)
library test.good.fi0215;
open protocol Foo {
@available(replaced=2, renamed="NewName")
strict OldName();
@available(added=2)
@selector("OldName")
strict NewName();
};
Notice that you must use @selector
to ensure the renamed method has the same
ABI.
See FIDL versioning to learn more about versioning.
fi-0216: Invalid @available(removed=N)
(ABI)
This is like fi-0205: Invalid @available(removed=N)
, but instead
of the element's name being reused, its ABI is:
@available(added=1)
library test.bad.fi0216;
open protocol Foo {
@available(removed=2)
strict Bar();
@available(added=2)
@selector("Bar")
flexible Qux();
};
If you are intentionally replacing the element's ABI, make it explicit by using
the arguments replaced
and renamed
instead of removed
:
@available(added=1)
library test.good.fi0216a;
open protocol Foo {
@available(replaced=2, renamed="Qux")
strict Bar();
@available(added=2)
@selector("Bar")
flexible Qux();
};
If you did not intend to reuse the ABI, choose a different one. In this case, we
can remove the @selector
attribute and use the method's default selector based
on its name:
@available(added=1)
library test.good.fi0216b;
open protocol Foo {
@available(removed=2)
strict Bar();
@available(added=2)
flexible Qux();
};
This error can occur for other members, not just methods. For bits and enum members, the ABI is the integer value. For struct members, the ABI is the byte offset. For table and union members, the ABI is the ordinal.
See FIDL versioning to learn more about versioning.
fi-0217: Invalid @available(replaced=N)
(ABI)
This is like fi-0206: Invalid @available(replaced=N)
, but for when
a replacement is only found matching the element's name, not its ABI:
@available(added=1)
library test.bad.fi0217;
open protocol Foo {
@available(replaced=2)
strict Bar();
@available(added=2)
@selector("NotBar")
flexible Bar();
};
If you intended to replace the element, make sure its ABI matches. In this case,
we can remove the @selector
attribute since both methods already have the same
name:
@available(added=1)
library test.good.fi0217a;
open protocol Foo {
@available(replaced=2)
strict Bar();
@available(added=2)
flexible Bar();
};
If you did not intend to replace the ABI, use removed
instead of replaced
.
In this case, we also have to choose a different name to avoid clashing with the
old one:
@available(added=1)
library test.good.fi0217b;
open protocol Foo {
@available(removed=2)
strict Bar();
@available(added=2)
flexible NotBar();
};
If you really want to reuse the name but not the ABI, use removed
instead of
replaced
, and also use renamed
to rename the old element post-removal,
freeing up its original name:
@available(added=1)
library test.good.fi0217c;
open protocol Foo {
@available(removed=2, renamed="DeprecatedBar")
strict Bar();
@available(added=2)
@selector("NotBar")
flexible Bar();
};
This error can occur for other members, not just methods. For bits and enum members, the ABI is the integer value. For struct members, the ABI is the byte offset. For table and union members, the ABI is the ordinal.
See FIDL versioning to learn more about versioning.
fi-0218: Invalid modifier availability argument
In the FIDL versioning modifier syntax, you can only use the arguments added
and removed
. Other @available
arguments like deprecated
are not allowed:
@available(added=1)
library test.bad.fi0218;
type Foo = resource(deprecated=2) struct {};
To fix the error, remove the unsupported argument:
@available(added=1)
library test.good.fi0218;
type Foo = resource struct {};
Unlike declarations and members, modifiers do not have lifecycles of their own, so concepts like deprecation, replacement, and renaming do not make sense for them. Modifiers can only be added and removed.
See FIDL versioning to learn more about versioning.
fi-0219: Cannot change method strictness
The FIDL versioning modifier syntax lets you add or remove strict
and
flexible
modifiers. However, this is not allowed on a two-way method if it
doesn't use the error
syntax, because such a change would be ABI breaking:
@available(added=1)
library test.bad.fi0219;
open protocol Foo {
strict(removed=2) flexible(added=2) Method() -> ();
};
Instead, remove the strict method and add a flexible method with a different name to replace it:
@available(added=1)
library test.good.fi0219a;
open protocol Foo {
@available(removed=2)
strict Method() -> ();
@available(added=2)
flexible NewMethod() -> ();
};
Alternatively, you can use the renamed
argument and @selector
attribute if you want to reuse the method name for the new ABI:
@available(added=1)
library test.good.fi0219b;
open protocol Foo {
@available(removed=2, renamed="StrictMethod")
strict Method() -> ();
@available(added=2)
@selector("FlexibleMethod")
flexible Method() -> ();
};
Changing the strictness of a two-way method without error syntax is not allowed because it would change the shape of the response. When a two-way method is flexible or uses error syntax, FIDL automatically generates a result union that wraps the response. Therefore, changing strictness is only safe for two-way methods that have error syntax.
See FIDL versioning to learn more about versioning.
fi-0220: Name not found in version range
This error occurs when a name cannot be found at a particular version, but it is
found in other versions. For example, an element Foo
added at version 1 cannot
reference another element Bar
added at version 2, because Bar
does not exist
at version 1:
@available(added=1)
library test.bad.fi0220;
alias Foo = Bar;
@available(added=2)
type Bar = struct {};
In this case, we can fix the error add an @available
attribute to make Foo
added at version 2 as well:
@available(added=1)
library test.good.fi0220;
@available(added=2)
alias Foo = Bar;
@available(added=2)
type Bar = struct {};
See FIDL versioning to learn more about versioning.