[fidl][dart][reland] Support for method errors.

Also make fidlgen_dart able to display template errors.

FIDL-521 #done

This is a reland of f98db6574ab0acd64331932a86a77e2f1a903d5f but it also
renames MethodError to MethodException to be more idiomatic.

Change-Id: I7cc40e9ff79e651f81fa70554f6e2587f22f79db
diff --git a/bin/fidl_bindings_test/fidl/bindings_test.fidl b/bin/fidl_bindings_test/fidl/bindings_test.fidl
index 480b679..9324fc6 100644
--- a/bin/fidl_bindings_test/fidl/bindings_test.fidl
+++ b/bin/fidl_bindings_test/fidl/bindings_test.fidl
@@ -63,6 +63,14 @@
 
     ReplySlowly(string value, float64 delaySeconds) -> (string value);
 
+    ReplyWithErrorZero(bool with_error) -> () error uint32;
+    ReplyWithErrorOne(bool with_error, string value) -> (string value) error uint32;
+    ReplyWithErrorMore(bool with_error, string value, bool other_value) -> (string value, bool other_value) error uint32;
+
+    ReplyWithErrorEnumZero(bool with_error) -> () error EnumOne;
+    ReplyWithErrorEnumOne(bool with_error, string value) -> (string value) error EnumOne;
+    ReplyWithErrorEnumMore(bool with_error, string value, bool other_value) -> (string value, bool other_value) error EnumOne;
+
     CloseConnection(float64 delaySeconds);
 
     -> NeverEvent();
diff --git a/bin/fidl_bindings_test/server/lib/main.dart b/bin/fidl_bindings_test/server/lib/main.dart
index 9d9b997..b43d11c 100644
--- a/bin/fidl_bindings_test/server/lib/main.dart
+++ b/bin/fidl_bindings_test/server/lib/main.dart
@@ -4,6 +4,7 @@
 
 import 'dart:async';
 
+import 'package:fidl/fidl.dart' show MethodException;
 import 'package:fidl_fidl_examples_bindingstest/fidl_async.dart';
 import 'package:fuchsia_services/services.dart';
 
@@ -150,6 +151,58 @@
   }
 
   @override
+  Future<void> replyWithErrorZero(bool withError) async {
+    if (withError) {
+      throw MethodException(23);
+    }
+  }
+
+  @override
+  Future<String> replyWithErrorOne(bool withError, String value) async {
+    if (withError) {
+      throw MethodException(42);
+    } else {
+      return value;
+    }
+  }
+
+  @override
+  Future<TestServer$ReplyWithErrorMore$Response> replyWithErrorMore(
+      bool withError, String value, bool otherValue) async {
+    if (withError) {
+      throw MethodException(666);
+    } else {
+      return TestServer$ReplyWithErrorMore$Response(value, otherValue);
+    }
+  }
+
+  @override
+  Future<void> replyWithErrorEnumZero(bool withError) async {
+    if (withError) {
+      throw MethodException(EnumOne.one);
+    }
+  }
+
+  @override
+  Future<String> replyWithErrorEnumOne(bool withError, String value) async {
+    if (withError) {
+      throw MethodException(EnumOne.two);
+    } else {
+      return value;
+    }
+  }
+
+  @override
+  Future<TestServer$ReplyWithErrorEnumMore$Response> replyWithErrorEnumMore(
+      bool withError, String value, bool otherValue) async {
+    if (withError) {
+      throw MethodException(EnumOne.three);
+    } else {
+      return TestServer$ReplyWithErrorEnumMore$Response(value, otherValue);
+    }
+  }
+
+  @override
   Future<void> closeConnection(double delaySeconds) async {
     if (delaySeconds == 0.0) {
       _binding.close();
diff --git a/bin/fidl_bindings_test/test/test/error_test.dart b/bin/fidl_bindings_test/test/test/error_test.dart
new file mode 100644
index 0000000..0400d75
--- /dev/null
+++ b/bin/fidl_bindings_test/test/test/error_test.dart
@@ -0,0 +1,74 @@
+// Copyright 2019 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.
+
+import 'package:test/test.dart';
+import 'package:fidl/fidl.dart' show MethodException;
+import 'package:fidl_fidl_examples_bindingstest/fidl_async.dart';
+
+import './server.dart';
+
+void main() async {
+  TestServerInstance server;
+
+  group('error', () {
+    setUpAll(() async {
+      server = TestServerInstance();
+      await server.start();
+    });
+
+    tearDownAll(() async {
+      await server.stop();
+      server = null;
+    });
+
+    test('zero arguments, int error', () {
+      expect(server.proxy.replyWithErrorZero(false), completion(anything));
+      expect(server.proxy.replyWithErrorZero(true),
+          throwsA(predicate((err) => err is MethodException && err.value == 23)));
+    });
+
+    test('one argument, int error', () {
+      expect(server.proxy.replyWithErrorOne(false, 'hello'),
+          completion(equals('hello')));
+      expect(server.proxy.replyWithErrorOne(true, 'hello'),
+          throwsA(predicate((err) => err is MethodException && err.value == 42)));
+    });
+
+    test('more arguments, int error', () {
+      expect(
+          server.proxy.replyWithErrorMore(false, 'hello', false),
+          completion(predicate(
+              (TestServer$ReplyWithErrorMore$Response response) =>
+                  response.value == 'hello' && response.otherValue == false)));
+      expect(server.proxy.replyWithErrorMore(true, 'hello', false),
+          throwsA(predicate((err) => err is MethodException && err.value == 666)));
+    });
+
+    test('zero arguments, enum error', () {
+      expect(server.proxy.replyWithErrorEnumZero(false), completion(anything));
+      expect(server.proxy.replyWithErrorEnumZero(true),
+          throwsA(predicate((err) => err is MethodException && err.value ==
+              EnumOne.one)));
+    });
+
+    test('one argument, enum error', () {
+      expect(server.proxy.replyWithErrorEnumOne(false, 'hello'),
+          completion(equals('hello')));
+      expect(server.proxy.replyWithErrorEnumOne(true, 'hello'),
+          throwsA(predicate((err) => err is MethodException && err.value ==
+              EnumOne.two)));
+    });
+
+    test('more arguments, enum error', () {
+      expect(
+          server.proxy.replyWithErrorEnumMore(false, 'hello', false),
+          completion(predicate(
+              (TestServer$ReplyWithErrorEnumMore$Response response) =>
+                  response.value == 'hello' && response.otherValue == false)));
+      expect(server.proxy.replyWithErrorEnumMore(true, 'hello', false),
+          throwsA(predicate((err) => err is MethodException && err.value ==
+              EnumOne.three)));
+    });
+  });
+}
diff --git a/bin/fidlgen_dart/backend/generator.go b/bin/fidlgen_dart/backend/generator.go
index 272809f..6163457 100644
--- a/bin/fidlgen_dart/backend/generator.go
+++ b/bin/fidlgen_dart/backend/generator.go
@@ -8,7 +8,7 @@
 	"fidl/compiler/backend/types"
 	"fidlgen_dart/backend/ir"
 	"fidlgen_dart/backend/templates"
-	"io"
+	"io/ioutil"
 	"os"
 	"os/exec"
 	"path/filepath"
@@ -19,38 +19,39 @@
 	templateName string,
 	tmpls *template.Template,
 	tree ir.Root, dartfmt string) error {
-	if err := os.MkdirAll(filepath.Dir(outputFilename), os.ModePerm); err != nil {
+	// Set up the output directory
+	outputDirectory := filepath.Dir(outputFilename)
+	if err := os.MkdirAll(outputDirectory, os.ModePerm); err != nil {
 		return err
 	}
-	generated, err := os.Create(outputFilename)
+
+	// Generate to a temporary file
+	temporaryFile, err := ioutil.TempFile(outputDirectory, "fidlgen_tmp")
 	if err != nil {
 		return err
 	}
-	defer generated.Close()
-	var f io.WriteCloser = generated
+	defer os.Remove(temporaryFile.Name())
+	err = tmpls.ExecuteTemplate(temporaryFile, templateName, tree)
+	if err != nil {
+		return err
+	}
+	err = temporaryFile.Close()
+	if err != nil {
+		return err
+	}
 
+	// Run dartfmt over the file
 	if dartfmt != "" {
-		// Pipe output via supplied dartfmt command.
-		cmd := exec.Command(dartfmt)
-		cmd.Stdout = generated
-		cmd.Stderr = nil
-		f, err = cmd.StdinPipe()
+		cmd := exec.Command(dartfmt, "--overwrite", temporaryFile.Name())
+		cmd.Stderr = os.Stderr
+		err = cmd.Run()
 		if err != nil {
 			return err
 		}
-		err = cmd.Start()
-		if err != nil {
-			return err
-		}
-
-		defer cmd.Wait()
 	}
 
-	err = tmpls.ExecuteTemplate(f, templateName, tree)
-	if err != nil {
-		return err
-	}
-	return f.Close()
+	// Rename the temporary file to the destination name
+	return os.Rename(temporaryFile.Name(), outputFilename)
 }
 
 // GenerateFidl generates Dart bindings from FIDL types structures.
diff --git a/bin/fidlgen_dart/backend/ir/ir.go b/bin/fidlgen_dart/backend/ir/ir.go
index 1aad626..1655c88 100644
--- a/bin/fidlgen_dart/backend/ir/ir.go
+++ b/bin/fidlgen_dart/backend/ir/ir.go
@@ -148,6 +148,15 @@
 	Documented
 }
 
+type MethodResponse struct {
+	WireParameters   []Parameter
+	MethodParameters []Parameter
+	HasError         bool
+	ResultType       Union
+	ValueType        Type
+	ErrorType        Type
+}
+
 // Method represents a method declaration within an interface declaration.
 type Method struct {
 	Ordinal            types.Ordinal
@@ -157,7 +166,7 @@
 	Request            []Parameter
 	RequestSize        int
 	HasResponse        bool
-	Response           []Parameter
+	Response           MethodResponse
 	ResponseSize       int
 	AsyncResponseClass string
 	AsyncResponseType  string
@@ -454,8 +463,9 @@
 }
 
 type compiler struct {
-	decls   *types.DeclMap
-	library types.LibraryIdentifier
+	decls     *types.DeclMap
+	library   types.LibraryIdentifier
+	typesRoot types.Root
 }
 
 func (c *compiler) inExternalLibrary(ci types.CompoundIdentifier) bool {
@@ -739,31 +749,147 @@
 	return e
 }
 
+func (c *compiler) compileParameter(paramName types.Identifier, paramType types.Type, offset int) Parameter {
+	var (
+		t         = c.compileType(paramType)
+		typeStr   = fmt.Sprintf("type: %s", t.typeExpr)
+		offsetStr = fmt.Sprintf("offset: %v", offset)
+		name      = c.compileLowerCamelIdentifier(paramName)
+		convert   string
+	)
+	if t.declType == types.InterfaceDeclType {
+		convert = "$fidl.convertInterfaceHandle"
+	} else if paramType.Kind == types.RequestType {
+		convert = "$fidl.convertInterfaceRequest"
+	}
+	return Parameter{
+		Type:     t,
+		Name:     name,
+		Offset:   offset,
+		Convert:  convert,
+		typeExpr: fmt.Sprintf("const $fidl.MemberType<%s>(%s, %s)", t.Decl, typeStr, offsetStr),
+	}
+}
+
 func (c *compiler) compileParameterArray(val []types.Parameter) []Parameter {
 	r := []Parameter{}
-
 	for _, v := range val {
-		t := c.compileType(v.Type)
-		typeStr := fmt.Sprintf("type: %s", t.typeExpr)
-		offsetStr := fmt.Sprintf("offset: %v", v.Offset)
-		name := c.compileLowerCamelIdentifier(v.Name)
-		convert := ""
-		if t.declType == types.InterfaceDeclType {
-			convert = "$fidl.convertInterfaceHandle"
-		} else if v.Type.Kind == types.RequestType {
-			convert = "$fidl.convertInterfaceRequest"
+		r = append(r, c.compileParameter(v.Name, v.Type, v.Offset))
+	}
+	return r
+}
+
+func (c *compiler) compileMethodResponse(method types.Method) MethodResponse {
+	var (
+		resultUnion *types.Union
+		resultType  types.Type
+		valueStruct *types.Struct
+		valueType   types.Type
+		isResult    bool
+		parameters  []Parameter
+	)
+
+	// Method needs to have exactly one response arg
+	if !method.HasResponse || len(method.Response) != 1 {
+		goto NotAResult
+	}
+	// That arg must be a non-nullable identifier
+	resultType = method.Response[0].Type
+	if resultType.Kind != types.IdentifierType || resultType.Nullable {
+		goto NotAResult
+	}
+	// That identifier is for a union
+	for _, union := range c.typesRoot.Unions {
+		if union.Name == resultType.Identifier {
+			resultUnion = &union
+			break
 		}
-		p := Parameter{
-			Type:     t,
-			Name:     name,
-			Offset:   v.Offset,
-			Convert:  convert,
-			typeExpr: fmt.Sprintf("const $fidl.MemberType<%s>(%s, %s)", t.Decl, typeStr, offsetStr),
-		}
-		r = append(r, p)
+	}
+	if resultUnion == nil {
+		goto NotAResult
+	}
+	// Union needs the [Result] attribute, two members
+	_, isResult = resultUnion.LookupAttribute("Result")
+	if !isResult || len(resultUnion.Members) != 2 {
+		goto NotAResult
 	}
 
-	return r
+	// Find the struct
+	valueType = resultUnion.Members[0].Type
+	for _, decl := range c.typesRoot.Structs {
+		if decl.Name == valueType.Identifier {
+			valueStruct = &decl
+			break
+		}
+	}
+	if valueStruct == nil {
+		goto NotAResult
+	}
+
+	// Turn the struct into a parameter array that will be used for function arguments.
+	for _, v := range valueStruct.Members {
+		parameters = append(parameters, c.compileParameter(v.Name, v.Type, v.Offset))
+	}
+
+	return MethodResponse{
+		WireParameters:   c.compileParameterArray(method.Response),
+		MethodParameters: parameters,
+		HasError:         true,
+		ResultType:       c.compileUnion(*resultUnion),
+		ValueType:        c.compileType(resultUnion.Members[0].Type),
+		ErrorType:        c.compileType(resultUnion.Members[1].Type),
+	}
+
+NotAResult:
+	response := c.compileParameterArray(method.Response)
+	return MethodResponse{
+		WireParameters:   response,
+		MethodParameters: response,
+	}
+}
+
+func (c *compiler) compileMethod(val types.Method, protocol Interface) Method {
+	var (
+		name               = c.compileLowerCamelIdentifier(val.Name)
+		request            = c.compileParameterArray(val.Request)
+		response           = c.compileMethodResponse(val)
+		asyncResponseClass string
+		asyncResponseType  string
+	)
+	if len(response.MethodParameters) > 1 {
+		asyncResponseClass = fmt.Sprintf("%s$%s$Response", protocol.Name, val.Name)
+	}
+	if val.HasResponse {
+		switch len(response.MethodParameters) {
+		case 0:
+			asyncResponseType = "void"
+		case 1:
+			responseType := response.MethodParameters[0].Type
+			asyncResponseType = responseType.Decl
+		default:
+			asyncResponseType = asyncResponseClass
+
+		}
+	}
+	_, transitional := val.LookupAttribute("Transitional")
+	return Method{
+		Ordinal:            val.Ordinal,
+		OrdinalName:        fmt.Sprintf("_k%s_%s_Ordinal", protocol.Name, val.Name),
+		Name:               name,
+		HasRequest:         val.HasRequest,
+		Request:            request,
+		RequestSize:        val.RequestSize,
+		HasResponse:        val.HasResponse,
+		Response:           response,
+		ResponseSize:       val.ResponseSize,
+		AsyncResponseClass: asyncResponseClass,
+		AsyncResponseType:  asyncResponseType,
+		CallbackType:       fmt.Sprintf("%s%sCallback", protocol.Name, val.Name),
+		TypeSymbol:         fmt.Sprintf("_k%s_%s_Type", protocol.Name, val.Name),
+		TypeExpr:           typeExprForMethod(request, response.WireParameters, fmt.Sprintf("%s.%s", protocol.Name, val.Name)),
+		Transitional:       transitional,
+		Documented:         docString(val),
+	}
 }
 
 func (c *compiler) compileInterface(val types.Interface) Interface {
@@ -785,47 +911,7 @@
 	}
 
 	for _, v := range val.Methods {
-		name := c.compileLowerCamelIdentifier(v.Name)
-		request := c.compileParameterArray(v.Request)
-		response := c.compileParameterArray(v.Response)
-		asyncResponseClass := ""
-		if len(response) > 1 {
-			asyncResponseClass = fmt.Sprintf("%s$%s$Response", r.Name, v.Name)
-		}
-		asyncResponseType := ""
-		if v.HasResponse {
-			if len(response) == 0 {
-				asyncResponseType = "void"
-			} else if len(response) == 1 {
-				responseType := response[0].Type
-				if responseType.SyncDecl != "" {
-					asyncResponseType = responseType.Decl
-				} else {
-					asyncResponseType = responseType.Decl
-				}
-			} else {
-				asyncResponseType = asyncResponseClass
-			}
-		}
-		_, transitional := v.LookupAttribute("Transitional")
-		m := Method{
-			v.Ordinal,
-			fmt.Sprintf("_k%s_%s_Ordinal", r.Name, v.Name),
-			name,
-			v.HasRequest,
-			request,
-			v.RequestSize,
-			v.HasResponse,
-			response,
-			v.ResponseSize,
-			asyncResponseClass,
-			asyncResponseType,
-			fmt.Sprintf("%s%sCallback", r.Name, v.Name),
-			fmt.Sprintf("_k%s_%s_Type", r.Name, v.Name),
-			typeExprForMethod(request, response, fmt.Sprintf("%s.%s", r.Name, v.Name)),
-			transitional,
-			docString(v),
-		}
+		m := c.compileMethod(v, r)
 		r.Methods = append(r.Methods, m)
 		if !v.HasRequest && v.HasResponse {
 			r.HasEvents = true
@@ -1009,7 +1095,11 @@
 // Compile the language independent type definition into the Dart-specific representation.
 func Compile(r types.Root) Root {
 	root := Root{}
-	c := compiler{&r.Decls, types.ParseLibraryName(r.Name)}
+	c := compiler{
+		decls:     &r.Decls,
+		library:   types.ParseLibraryName(r.Name),
+		typesRoot: r,
+	}
 
 	root.LibraryName = fmt.Sprintf("fidl_%s", formatLibraryName(c.library))
 
diff --git a/bin/fidlgen_dart/backend/templates/interface.tmpl.go b/bin/fidlgen_dart/backend/templates/interface.tmpl.go
index ec9fcd3..082a44b 100644
--- a/bin/fidlgen_dart/backend/templates/interface.tmpl.go
+++ b/bin/fidlgen_dart/backend/templates/interface.tmpl.go
@@ -14,14 +14,14 @@
 
 {{- define "RequestMethodSignature" -}}
   {{- if .HasResponse -}}
-{{ .Name }}({{ template "Params" .Request }}{{ if .Request }}, {{ end }}void callback({{ template "Params" .Response }}))
+{{ .Name }}({{ template "Params" .Request }}{{ if .Request }}, {{ end }}void callback({{ template "Params" .Response.WireParameters }}))
   {{- else -}}
 {{ .Name }}({{ template "Params" .Request }})
   {{- end -}}
 {{ end -}}
 
 {{- define "ResponseMethodSignature" -}}
-{{ .Name }}({{ template "Params" .Response }})
+{{ .Name }}({{ template "Params" .Response.WireParameters }})
 {{ end -}}
 
 {{- define "InterfaceDeclaration" -}}
@@ -36,7 +36,7 @@
 }
 
 {{ range .Methods }}
-// {{ .Name }}: {{ if .HasRequest }}({{ template "Params" .Request }}){{ end }}{{ if .HasResponse }} -> ({{ template "Params" .Response }}){{ end }}
+// {{ .Name }}: {{ if .HasRequest }}({{ template "Params" .Request }}){{ end }}{{ if .HasResponse }} -> ({{ template "Params" .Response.WireParameters }}){{ end }}
 const int {{ .OrdinalName }} = {{ .Ordinal }};
 const $fidl.MethodType {{ .TypeSymbol }} = {{ .TypeExpr }};
 {{- end }}
@@ -44,7 +44,7 @@
 {{ range .Methods }}
   {{- if not .HasRequest }}
     {{- if .HasResponse }}
-typedef void {{ .CallbackType }}({{ template "Params" .Response }});
+typedef void {{ .CallbackType }}({{ template "Params" .Response.WireParameters }});
     {{- end }}
   {{- end }}
 {{- end }}
@@ -74,7 +74,7 @@
           final List<$fidl.MemberType> $types = {{ .TypeSymbol }}.response;
           $decoder.claimMemory({{ .ResponseSize }});
           $callback(
-      {{- range $index, $response := .Response }}
+      {{- range $index, $response := .Response.WireParameters }}
             $types[{{ $index }}].decode($decoder, 0),
       {{- end }}
           );
@@ -120,7 +120,7 @@
           final List<$fidl.MemberType> $types = {{ .TypeSymbol }}.response;
           $decoder.claimMemory({{ .ResponseSize }});
           $callback(
-        {{- range $index, $response := .Response }}
+        {{- range $index, $response := .Response.WireParameters }}
             $types[{{ $index }}].decode($decoder, 0),
         {{- end }}
           );
@@ -168,11 +168,11 @@
       $zonedCallback = callback;
     } else {
       Zone $z = Zone.current;
-      {{- if .Response }}
-      $zonedCallback = (({{ template "Params" .Response }}) {
+      {{- if .Response.WireParameters }}
+      $zonedCallback = (({{ template "Params" .Response.WireParameters }}) {
         $z.bindCallback(() {
           callback(
-        {{- range .Response -}}
+        {{- range .Response.WireParameters -}}
             {{ .Name }},
         {{- end -}}
           );
@@ -204,11 +204,11 @@
   void {{ template "ResponseMethodSignature" . }} {
     final $fidl.Encoder $encoder = new $fidl.Encoder();
     $encoder.encodeMessageHeader({{ .OrdinalName }}, 0);
-      {{- if .Response }}
+      {{- if .Response.WireParameters }}
     $encoder.alloc({{ .ResponseSize }} - $fidl.kMessageHeaderSize);
     final List<$fidl.MemberType> $types = {{ .TypeSymbol }}.response;
       {{- end }}
-      {{- range $index, $response := .Response }}
+      {{- range $index, $response := .Response.WireParameters }}
     $types[{{ $index }}].encode($encoder, {{ .Name }}, 0);
       {{- end }}
     _binding.sendMessage($encoder.message);
@@ -233,14 +233,14 @@
   {{- if .HasRequest }}
     {{- if .HasResponse }}
   Function _{{ .Name }}Responder($fidl.MessageSink $respond, int $txid) {
-    return ({{ template "Params" .Response }}) {
+    return ({{ template "Params" .Response.WireParameters }}) {
       final $fidl.Encoder $encoder = new $fidl.Encoder();
       $encoder.encodeMessageHeader({{ .OrdinalName }}, $txid);
-      {{- if .Response }}
+      {{- if .Response.WireParameters }}
       $encoder.alloc({{ .ResponseSize }} - $fidl.kMessageHeaderSize);
       final List<$fidl.MemberType> $types = {{ .TypeSymbol }}.response;
       {{- end }}
-      {{- range $index, $response := .Response }}
+      {{- range $index, $response := .Response.WireParameters }}
       $types[{{ $index }}].encode($encoder, {{ .Name }}, 0);
       {{- end }}
       $respond($encoder.message);
@@ -339,17 +339,21 @@
   This template expands to an expression so it can be assigned or passed as an argument.
 */}}
 {{- define "DecodeResponse" -}}
-  {{- if .AsyncResponseClass -}}
-    new {{ .AsyncResponseClass }}(
-      {{- range $index, $response := .Response }}
-        $types[{{ $index }}].decode($decoder, 0),
-      {{- end -}}
-    )
-  {{- else -}}
-    {{- if .Response -}}
-      $types[0].decode($decoder, 0),
+  {{- if .Response.HasError }}
+    $types[0].decode($decoder, 0)
+  {{- else }}
+    {{- if .AsyncResponseClass -}}
+      new {{ .AsyncResponseClass }}(
+        {{- range $index, $response := .Response.WireParameters }}
+          $types[{{ $index }}].decode($decoder, 0),
+        {{- end -}}
+      )
     {{- else -}}
-      null
+      {{- if .Response.WireParameters -}}
+        $types[0].decode($decoder, 0)
+      {{- else -}}
+        null
+      {{- end -}}
     {{- end -}}
   {{- end -}}
 {{ end -}}
@@ -365,12 +369,12 @@
   This template expands to a statement.
 */}}
 {{- define "EncodeResponse" -}}
-  {{- if .AsyncResponseClass -}}
-    {{- range $index, $response := .Response }}
+  {{- if (and .AsyncResponseClass (not .Response.HasError)) -}}
+    {{- range $index, $response := .Response.WireParameters }}
       $types[{{ $index }}].encode($encoder, $response.{{ .Name }}, 0);
     {{- end }}
   {{- else -}}
-    {{- if .Response -}}
+    {{- if .Response.WireParameters -}}
       $types[0].encode($encoder, $response, 0);
     {{- end -}}
   {{- end -}}
@@ -380,7 +384,7 @@
 
 {{ range .Methods }}
 // {{ .Name }}: {{ if .HasRequest }}({{ template "AsyncParams" .Request }}){{ end -}}
-                {{- if .HasResponse }} -> ({{ template "AsyncParams" .Response }}){{ end }}
+                {{- if .HasResponse }} -> ({{ template "AsyncParams" .Response.MethodParameters }}){{ end }}
 const int {{ .OrdinalName }} = {{ .Ordinal }};
 const $fidl.MethodType {{ .TypeSymbol }} = {{ .TypeExpr }};
 {{- end }}
@@ -388,11 +392,11 @@
 {{- range .Methods }}
   {{- if .AsyncResponseClass }}
 class {{ .AsyncResponseClass }} {
-    {{- range .Response }}
+    {{- range .Response.MethodParameters }}
   final {{ .Type.Decl }} {{ .Name }};
     {{- end }}
   {{ .AsyncResponseClass }}(
-    {{- range .Response }}
+    {{- range .Response.MethodParameters }}
       this.{{ .Name }},
     {{- end -}}
     );
@@ -400,6 +404,7 @@
   {{- end }}
 {{- end }}
 
+
 {{- range .Doc }}
 ///{{ . -}}
 {{- end }}
@@ -529,9 +534,30 @@
           Timeline.startSync(_name);
           final List<$fidl.MemberType> $types = {{ .TypeSymbol }}.response;
           $decoder.claimMemory({{ .ResponseSize }});
-          $completer.complete(
-            {{- template "DecodeResponse" . -}}
-          );
+          // ignore: prefer_const_declarations
+          final $response = {{- template "DecodeResponse" . -}};
+          {{ if .Response.HasError }}
+            if ($response.tag == {{ .Response.ResultType.TagName }}.response) {
+              {{ if .AsyncResponseClass }}
+                $completer.complete(
+                  {{ .AsyncResponseClass }}(
+                  {{ range $param := .Response.MethodParameters }}
+                    $response.response.{{ $param.Name }},
+                  {{ end }}
+                  ));
+              {{ else }}
+                {{ if (eq .AsyncResponseType "void") }}
+                  $completer.complete(null);
+                {{ else }}
+                  $completer.complete($response.response.{{ (index .Response.MethodParameters 0).Name }});
+                {{ end }}
+              {{ end }}
+            } else {
+              $completer.completeError($fidl.MethodException($response.err));
+            }
+          {{ else }}
+            $completer.complete($response);
+          {{ end }}
         // ignore: avoid_catches_without_on_clauses
         } catch(_e) {
           ctrl.proxyError(new $fidl.FidlError('Exception handling method response $_name: $_e'));
@@ -643,10 +669,36 @@
               {{- end }});
 
               {{- if .HasResponse }}
-                $future.then(($response) {
+                $future
+                {{ if .Response.HasError }}
+                .then(($responseValue) {
+                  {{ if .AsyncResponseClass }}
+                    return {{ .Response.ResultType.Name }}.withResponse(
+                      {{ .Response.ValueType.Decl }}(
+                      {{ range $param := .Response.MethodParameters }}
+                        {{ $param.Name }}: $responseValue.{{ $param.Name }},
+                      {{ end }}
+                      ));
+                  {{ else }}
+                    return {{ .Response.ResultType.Name }}.withResponse(
+                      {{ .Response.ValueType.Decl }}(
+                        {{ if (ne .AsyncResponseType "void") }}
+                          {{ (index .Response.MethodParameters 0).Name }}: $responseValue
+                        {{ end }}
+                        ));
+                  {{ end }}
+                }, onError: ($error) {
+                  if ($error is $fidl.MethodException) {
+                    return {{ .Response.ResultType.Name }}.withErr($error.value);
+                  } else {
+                    return Future.error($error);
+                  }
+                })
+                {{ end }}
+                .then(($response) {
                   final $fidl.Encoder $encoder = new $fidl.Encoder();
                   $encoder.encodeMessageHeader({{ .OrdinalName }}, $message.txid);
-                  {{- if .Response }}
+                  {{- if .Response.WireParameters }}
                     $encoder.alloc({{ .ResponseSize }} - $fidl.kMessageHeaderSize);
                     final List<$fidl.MemberType> $types = {{ .TypeSymbol }}.response;
                     {{ template "EncodeResponse" . -}}
diff --git a/bin/fidlgen_dart/goldens/doc_comments.test.fidl.json_async.dart.golden b/bin/fidlgen_dart/goldens/doc_comments.test.fidl.json_async.dart.golden
index f5562dd..8eee772 100644
--- a/bin/fidlgen_dart/goldens/doc_comments.test.fidl.json_async.dart.golden
+++ b/bin/fidlgen_dart/goldens/doc_comments.test.fidl.json_async.dart.golden
@@ -279,7 +279,9 @@
   Future<void> method() async {
     if (!ctrl.isBound) {
       return new Future.error(
-          new $fidl.FidlStateException('The proxy is closed.'));
+          new $fidl.FidlStateException(
+              'Proxy<${ctrl.$interfaceName}> is closed.'),
+          StackTrace.current);
     }
 
     final $fidl.Encoder $encoder = new $fidl.Encoder();
diff --git a/bin/fidlgen_dart/goldens/ordinal_switch.test.fidl.json_async.dart.golden b/bin/fidlgen_dart/goldens/ordinal_switch.test.fidl.json_async.dart.golden
index 9ac1729..fbc34be 100644
--- a/bin/fidlgen_dart/goldens/ordinal_switch.test.fidl.json_async.dart.golden
+++ b/bin/fidlgen_dart/goldens/ordinal_switch.test.fidl.json_async.dart.golden
@@ -192,7 +192,9 @@
   Future<void> ordinalFive() async {
     if (!ctrl.isBound) {
       return new Future.error(
-          new $fidl.FidlStateException('The proxy is closed.'));
+          new $fidl.FidlStateException(
+              'Proxy<${ctrl.$interfaceName}> is closed.'),
+          StackTrace.current);
     }
 
     final $fidl.Encoder $encoder = new $fidl.Encoder();
@@ -206,7 +208,9 @@
   Future<void> onlyGeneratedOrdinal() async {
     if (!ctrl.isBound) {
       return new Future.error(
-          new $fidl.FidlStateException('The proxy is closed.'));
+          new $fidl.FidlStateException(
+              'Proxy<${ctrl.$interfaceName}> is closed.'),
+          StackTrace.current);
     }
 
     final $fidl.Encoder $encoder = new $fidl.Encoder();
diff --git a/bin/fidlgen_dart/goldens/protocols.test.fidl.json b/bin/fidlgen_dart/goldens/protocols.test.fidl.json
index 3f24661..caa3766 100644
--- a/bin/fidlgen_dart/goldens/protocols.test.fidl.json
+++ b/bin/fidlgen_dart/goldens/protocols.test.fidl.json
@@ -4,7 +4,49 @@
   "library_dependencies": [],
   "bits_declarations": [],
   "const_declarations": [],
-  "enum_declarations": [],
+  "enum_declarations": [
+    {
+      "name": "test.name/ErrorEnun",
+      "location": {
+        "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
+        "line": 14,
+        "column": 5
+      },
+      "type": "uint32",
+      "members": [
+        {
+          "name": "ERR_FOO",
+          "location": {
+            "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
+            "line": 15,
+            "column": 4
+          },
+          "value": {
+            "kind": "literal",
+            "literal": {
+              "kind": "numeric",
+              "value": "1"
+            }
+          }
+        },
+        {
+          "name": "ERR_BAR",
+          "location": {
+            "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
+            "line": 16,
+            "column": 4
+          },
+          "value": {
+            "kind": "literal",
+            "literal": {
+              "kind": "numeric",
+              "value": "2"
+            }
+          }
+        }
+      ]
+    }
+  ],
   "interface_declarations": [
     {
       "name": "test.name/WithAndWithoutRequestResponse",
@@ -257,10 +299,94 @@
       ]
     },
     {
+      "name": "test.name/WithErrorSyntax",
+      "location": {
+        "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
+        "line": 19,
+        "column": 9
+      },
+      "methods": [
+        {
+          "ordinal": 2069369145,
+          "generated_ordinal": 2069369145,
+          "name": "ErrorAsPrimitive",
+          "location": {
+            "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
+            "line": 20,
+            "column": 4
+          },
+          "has_request": true,
+          "maybe_request": [],
+          "maybe_request_size": 16,
+          "maybe_request_alignment": 8,
+          "has_response": true,
+          "maybe_response": [
+            {
+              "type": {
+                "kind": "identifier",
+                "identifier": "test.name/WithErrorSyntax_ErrorAsPrimitive_Result",
+                "nullable": false
+              },
+              "name": "result",
+              "location": {
+                "filename": "generated",
+                "line": 18,
+                "column": 0
+              },
+              "size": 8,
+              "max_out_of_line": 0,
+              "alignment": 4,
+              "offset": 16,
+              "max_handles": 0
+            }
+          ],
+          "maybe_response_size": 24,
+          "maybe_response_alignment": 8
+        },
+        {
+          "ordinal": 1284890143,
+          "generated_ordinal": 1284890143,
+          "name": "ErrorAsEnum",
+          "location": {
+            "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
+            "line": 21,
+            "column": 4
+          },
+          "has_request": true,
+          "maybe_request": [],
+          "maybe_request_size": 16,
+          "maybe_request_alignment": 8,
+          "has_response": true,
+          "maybe_response": [
+            {
+              "type": {
+                "kind": "identifier",
+                "identifier": "test.name/WithErrorSyntax_ErrorAsEnum_Result",
+                "nullable": false
+              },
+              "name": "result",
+              "location": {
+                "filename": "generated",
+                "line": 25,
+                "column": 0
+              },
+              "size": 8,
+              "max_out_of_line": 0,
+              "alignment": 4,
+              "offset": 16,
+              "max_handles": 0
+            }
+          ],
+          "maybe_response_size": 24,
+          "maybe_response_alignment": 8
+        }
+      ]
+    },
+    {
       "name": "test.name/OvernetInternalProtocol",
       "location": {
         "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
-        "line": 15,
+        "line": 25,
         "column": 9
       },
       "maybe_attributes": [
@@ -276,7 +402,7 @@
           "name": "MethodA",
           "location": {
             "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
-            "line": 16,
+            "line": 26,
             "column": 4
           },
           "has_request": true,
@@ -289,7 +415,7 @@
               "name": "a",
               "location": {
                 "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
-                "line": 16,
+                "line": 26,
                 "column": 18
               },
               "size": 8,
@@ -306,7 +432,7 @@
               "name": "b",
               "location": {
                 "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
-                "line": 16,
+                "line": 26,
                 "column": 27
               },
               "size": 8,
@@ -326,7 +452,7 @@
           "name": "EventA",
           "location": {
             "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
-            "line": 17,
+            "line": 27,
             "column": 7
           },
           "has_request": false,
@@ -340,7 +466,7 @@
               "name": "a",
               "location": {
                 "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
-                "line": 17,
+                "line": 27,
                 "column": 20
               },
               "size": 8,
@@ -357,7 +483,7 @@
               "name": "b",
               "location": {
                 "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
-                "line": 17,
+                "line": 27,
                 "column": 29
               },
               "size": 8,
@@ -376,7 +502,7 @@
           "name": "MethodB",
           "location": {
             "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
-            "line": 18,
+            "line": 28,
             "column": 4
           },
           "has_request": true,
@@ -389,7 +515,7 @@
               "name": "a",
               "location": {
                 "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
-                "line": 18,
+                "line": 28,
                 "column": 18
               },
               "size": 8,
@@ -406,7 +532,7 @@
               "name": "b",
               "location": {
                 "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
-                "line": 18,
+                "line": 28,
                 "column": 27
               },
               "size": 8,
@@ -428,7 +554,7 @@
               "name": "result",
               "location": {
                 "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
-                "line": 18,
+                "line": 28,
                 "column": 40
               },
               "size": 8,
@@ -447,7 +573,7 @@
       "name": "test.name/SocketControlProtocol",
       "location": {
         "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
-        "line": 22,
+        "line": 32,
         "column": 9
       },
       "maybe_attributes": [
@@ -463,7 +589,7 @@
           "name": "MethodA",
           "location": {
             "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
-            "line": 23,
+            "line": 33,
             "column": 4
           },
           "has_request": true,
@@ -476,7 +602,7 @@
               "name": "a",
               "location": {
                 "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
-                "line": 23,
+                "line": 33,
                 "column": 18
               },
               "size": 8,
@@ -493,7 +619,7 @@
               "name": "b",
               "location": {
                 "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
-                "line": 23,
+                "line": 33,
                 "column": 27
               },
               "size": 8,
@@ -513,7 +639,7 @@
           "name": "EventA",
           "location": {
             "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
-            "line": 24,
+            "line": 34,
             "column": 7
           },
           "has_request": false,
@@ -527,7 +653,7 @@
               "name": "a",
               "location": {
                 "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
-                "line": 24,
+                "line": 34,
                 "column": 20
               },
               "size": 8,
@@ -544,7 +670,7 @@
               "name": "b",
               "location": {
                 "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
-                "line": 24,
+                "line": 34,
                 "column": 29
               },
               "size": 8,
@@ -563,7 +689,7 @@
           "name": "MethodB",
           "location": {
             "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
-            "line": 25,
+            "line": 35,
             "column": 4
           },
           "has_request": true,
@@ -576,7 +702,7 @@
               "name": "a",
               "location": {
                 "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
-                "line": 25,
+                "line": 35,
                 "column": 18
               },
               "size": 8,
@@ -593,7 +719,7 @@
               "name": "b",
               "location": {
                 "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
-                "line": 25,
+                "line": 35,
                 "column": 27
               },
               "size": 8,
@@ -615,7 +741,7 @@
               "name": "result",
               "location": {
                 "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
-                "line": 25,
+                "line": 35,
                 "column": 40
               },
               "size": 8,
@@ -634,7 +760,7 @@
       "name": "test.name/ChannelProtocol",
       "location": {
         "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
-        "line": 29,
+        "line": 39,
         "column": 9
       },
       "maybe_attributes": [
@@ -650,293 +776,6 @@
           "name": "MethodA",
           "location": {
             "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
-            "line": 30,
-            "column": 4
-          },
-          "has_request": true,
-          "maybe_request": [
-            {
-              "type": {
-                "kind": "primitive",
-                "subtype": "int64"
-              },
-              "name": "a",
-              "location": {
-                "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
-                "line": 30,
-                "column": 18
-              },
-              "size": 8,
-              "max_out_of_line": 0,
-              "alignment": 8,
-              "offset": 16,
-              "max_handles": 0
-            },
-            {
-              "type": {
-                "kind": "primitive",
-                "subtype": "int64"
-              },
-              "name": "b",
-              "location": {
-                "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
-                "line": 30,
-                "column": 27
-              },
-              "size": 8,
-              "max_out_of_line": 0,
-              "alignment": 8,
-              "offset": 24,
-              "max_handles": 0
-            }
-          ],
-          "maybe_request_size": 32,
-          "maybe_request_alignment": 8,
-          "has_response": false
-        },
-        {
-          "ordinal": 477676034,
-          "generated_ordinal": 477676034,
-          "name": "EventA",
-          "location": {
-            "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
-            "line": 31,
-            "column": 7
-          },
-          "has_request": false,
-          "has_response": true,
-          "maybe_response": [
-            {
-              "type": {
-                "kind": "primitive",
-                "subtype": "int64"
-              },
-              "name": "a",
-              "location": {
-                "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
-                "line": 31,
-                "column": 20
-              },
-              "size": 8,
-              "max_out_of_line": 0,
-              "alignment": 8,
-              "offset": 16,
-              "max_handles": 0
-            },
-            {
-              "type": {
-                "kind": "primitive",
-                "subtype": "int64"
-              },
-              "name": "b",
-              "location": {
-                "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
-                "line": 31,
-                "column": 29
-              },
-              "size": 8,
-              "max_out_of_line": 0,
-              "alignment": 8,
-              "offset": 24,
-              "max_handles": 0
-            }
-          ],
-          "maybe_response_size": 32,
-          "maybe_response_alignment": 8
-        },
-        {
-          "ordinal": 180770075,
-          "generated_ordinal": 180770075,
-          "name": "MethodB",
-          "location": {
-            "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
-            "line": 32,
-            "column": 4
-          },
-          "has_request": true,
-          "maybe_request": [
-            {
-              "type": {
-                "kind": "primitive",
-                "subtype": "int64"
-              },
-              "name": "a",
-              "location": {
-                "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
-                "line": 32,
-                "column": 18
-              },
-              "size": 8,
-              "max_out_of_line": 0,
-              "alignment": 8,
-              "offset": 16,
-              "max_handles": 0
-            },
-            {
-              "type": {
-                "kind": "primitive",
-                "subtype": "int64"
-              },
-              "name": "b",
-              "location": {
-                "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
-                "line": 32,
-                "column": 27
-              },
-              "size": 8,
-              "max_out_of_line": 0,
-              "alignment": 8,
-              "offset": 24,
-              "max_handles": 0
-            }
-          ],
-          "maybe_request_size": 32,
-          "maybe_request_alignment": 8,
-          "has_response": true,
-          "maybe_response": [
-            {
-              "type": {
-                "kind": "primitive",
-                "subtype": "int64"
-              },
-              "name": "result",
-              "location": {
-                "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
-                "line": 32,
-                "column": 40
-              },
-              "size": 8,
-              "max_out_of_line": 0,
-              "alignment": 8,
-              "offset": 16,
-              "max_handles": 0
-            }
-          ],
-          "maybe_response_size": 24,
-          "maybe_response_alignment": 8
-        }
-      ]
-    },
-    {
-      "name": "test.name/KitchenSink",
-      "location": {
-        "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
-        "line": 37,
-        "column": 9
-      },
-      "maybe_attributes": [
-        {
-          "name": "Transport",
-          "value": "Channel, SocketControl, OvernetInternal"
-        }
-      ],
-      "methods": [
-        {
-          "ordinal": 450577456,
-          "generated_ordinal": 450577456,
-          "name": "MethodA",
-          "location": {
-            "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
-            "line": 38,
-            "column": 4
-          },
-          "has_request": true,
-          "maybe_request": [
-            {
-              "type": {
-                "kind": "primitive",
-                "subtype": "int64"
-              },
-              "name": "a",
-              "location": {
-                "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
-                "line": 38,
-                "column": 18
-              },
-              "size": 8,
-              "max_out_of_line": 0,
-              "alignment": 8,
-              "offset": 16,
-              "max_handles": 0
-            },
-            {
-              "type": {
-                "kind": "primitive",
-                "subtype": "int64"
-              },
-              "name": "b",
-              "location": {
-                "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
-                "line": 38,
-                "column": 27
-              },
-              "size": 8,
-              "max_out_of_line": 0,
-              "alignment": 8,
-              "offset": 24,
-              "max_handles": 0
-            }
-          ],
-          "maybe_request_size": 32,
-          "maybe_request_alignment": 8,
-          "has_response": false
-        },
-        {
-          "ordinal": 1795426833,
-          "generated_ordinal": 1795426833,
-          "name": "EventA",
-          "location": {
-            "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
-            "line": 39,
-            "column": 7
-          },
-          "has_request": false,
-          "has_response": true,
-          "maybe_response": [
-            {
-              "type": {
-                "kind": "primitive",
-                "subtype": "int64"
-              },
-              "name": "a",
-              "location": {
-                "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
-                "line": 39,
-                "column": 20
-              },
-              "size": 8,
-              "max_out_of_line": 0,
-              "alignment": 8,
-              "offset": 16,
-              "max_handles": 0
-            },
-            {
-              "type": {
-                "kind": "primitive",
-                "subtype": "int64"
-              },
-              "name": "b",
-              "location": {
-                "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
-                "line": 39,
-                "column": 29
-              },
-              "size": 8,
-              "max_out_of_line": 0,
-              "alignment": 8,
-              "offset": 24,
-              "max_handles": 0
-            }
-          ],
-          "maybe_response_size": 32,
-          "maybe_response_alignment": 8
-        },
-        {
-          "ordinal": 1999489700,
-          "generated_ordinal": 1999489700,
-          "name": "MethodB",
-          "location": {
-            "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
             "line": 40,
             "column": 4
           },
@@ -979,6 +818,106 @@
           ],
           "maybe_request_size": 32,
           "maybe_request_alignment": 8,
+          "has_response": false
+        },
+        {
+          "ordinal": 477676034,
+          "generated_ordinal": 477676034,
+          "name": "EventA",
+          "location": {
+            "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
+            "line": 41,
+            "column": 7
+          },
+          "has_request": false,
+          "has_response": true,
+          "maybe_response": [
+            {
+              "type": {
+                "kind": "primitive",
+                "subtype": "int64"
+              },
+              "name": "a",
+              "location": {
+                "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
+                "line": 41,
+                "column": 20
+              },
+              "size": 8,
+              "max_out_of_line": 0,
+              "alignment": 8,
+              "offset": 16,
+              "max_handles": 0
+            },
+            {
+              "type": {
+                "kind": "primitive",
+                "subtype": "int64"
+              },
+              "name": "b",
+              "location": {
+                "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
+                "line": 41,
+                "column": 29
+              },
+              "size": 8,
+              "max_out_of_line": 0,
+              "alignment": 8,
+              "offset": 24,
+              "max_handles": 0
+            }
+          ],
+          "maybe_response_size": 32,
+          "maybe_response_alignment": 8
+        },
+        {
+          "ordinal": 180770075,
+          "generated_ordinal": 180770075,
+          "name": "MethodB",
+          "location": {
+            "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
+            "line": 42,
+            "column": 4
+          },
+          "has_request": true,
+          "maybe_request": [
+            {
+              "type": {
+                "kind": "primitive",
+                "subtype": "int64"
+              },
+              "name": "a",
+              "location": {
+                "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
+                "line": 42,
+                "column": 18
+              },
+              "size": 8,
+              "max_out_of_line": 0,
+              "alignment": 8,
+              "offset": 16,
+              "max_handles": 0
+            },
+            {
+              "type": {
+                "kind": "primitive",
+                "subtype": "int64"
+              },
+              "name": "b",
+              "location": {
+                "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
+                "line": 42,
+                "column": 27
+              },
+              "size": 8,
+              "max_out_of_line": 0,
+              "alignment": 8,
+              "offset": 24,
+              "max_handles": 0
+            }
+          ],
+          "maybe_request_size": 32,
+          "maybe_request_alignment": 8,
           "has_response": true,
           "maybe_response": [
             {
@@ -989,7 +928,194 @@
               "name": "result",
               "location": {
                 "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
-                "line": 40,
+                "line": 42,
+                "column": 40
+              },
+              "size": 8,
+              "max_out_of_line": 0,
+              "alignment": 8,
+              "offset": 16,
+              "max_handles": 0
+            }
+          ],
+          "maybe_response_size": 24,
+          "maybe_response_alignment": 8
+        }
+      ]
+    },
+    {
+      "name": "test.name/KitchenSink",
+      "location": {
+        "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
+        "line": 47,
+        "column": 9
+      },
+      "maybe_attributes": [
+        {
+          "name": "Transport",
+          "value": "Channel, SocketControl, OvernetInternal"
+        }
+      ],
+      "methods": [
+        {
+          "ordinal": 450577456,
+          "generated_ordinal": 450577456,
+          "name": "MethodA",
+          "location": {
+            "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
+            "line": 48,
+            "column": 4
+          },
+          "has_request": true,
+          "maybe_request": [
+            {
+              "type": {
+                "kind": "primitive",
+                "subtype": "int64"
+              },
+              "name": "a",
+              "location": {
+                "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
+                "line": 48,
+                "column": 18
+              },
+              "size": 8,
+              "max_out_of_line": 0,
+              "alignment": 8,
+              "offset": 16,
+              "max_handles": 0
+            },
+            {
+              "type": {
+                "kind": "primitive",
+                "subtype": "int64"
+              },
+              "name": "b",
+              "location": {
+                "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
+                "line": 48,
+                "column": 27
+              },
+              "size": 8,
+              "max_out_of_line": 0,
+              "alignment": 8,
+              "offset": 24,
+              "max_handles": 0
+            }
+          ],
+          "maybe_request_size": 32,
+          "maybe_request_alignment": 8,
+          "has_response": false
+        },
+        {
+          "ordinal": 1795426833,
+          "generated_ordinal": 1795426833,
+          "name": "EventA",
+          "location": {
+            "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
+            "line": 49,
+            "column": 7
+          },
+          "has_request": false,
+          "has_response": true,
+          "maybe_response": [
+            {
+              "type": {
+                "kind": "primitive",
+                "subtype": "int64"
+              },
+              "name": "a",
+              "location": {
+                "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
+                "line": 49,
+                "column": 20
+              },
+              "size": 8,
+              "max_out_of_line": 0,
+              "alignment": 8,
+              "offset": 16,
+              "max_handles": 0
+            },
+            {
+              "type": {
+                "kind": "primitive",
+                "subtype": "int64"
+              },
+              "name": "b",
+              "location": {
+                "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
+                "line": 49,
+                "column": 29
+              },
+              "size": 8,
+              "max_out_of_line": 0,
+              "alignment": 8,
+              "offset": 24,
+              "max_handles": 0
+            }
+          ],
+          "maybe_response_size": 32,
+          "maybe_response_alignment": 8
+        },
+        {
+          "ordinal": 1999489700,
+          "generated_ordinal": 1999489700,
+          "name": "MethodB",
+          "location": {
+            "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
+            "line": 50,
+            "column": 4
+          },
+          "has_request": true,
+          "maybe_request": [
+            {
+              "type": {
+                "kind": "primitive",
+                "subtype": "int64"
+              },
+              "name": "a",
+              "location": {
+                "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
+                "line": 50,
+                "column": 18
+              },
+              "size": 8,
+              "max_out_of_line": 0,
+              "alignment": 8,
+              "offset": 16,
+              "max_handles": 0
+            },
+            {
+              "type": {
+                "kind": "primitive",
+                "subtype": "int64"
+              },
+              "name": "b",
+              "location": {
+                "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
+                "line": 50,
+                "column": 27
+              },
+              "size": 8,
+              "max_out_of_line": 0,
+              "alignment": 8,
+              "offset": 24,
+              "max_handles": 0
+            }
+          ],
+          "maybe_request_size": 32,
+          "maybe_request_alignment": 8,
+          "has_response": true,
+          "maybe_response": [
+            {
+              "type": {
+                "kind": "primitive",
+                "subtype": "int64"
+              },
+              "name": "result",
+              "location": {
+                "filename": "garnet/go/src/fidl/compiler/backend/typestest/protocols.test.fidl",
+                "line": 50,
                 "column": 40
               },
               "size": 8,
@@ -1005,22 +1131,171 @@
       ]
     }
   ],
-  "struct_declarations": [],
+  "struct_declarations": [
+    {
+      "name": "test.name/WithErrorSyntax_ErrorAsPrimitive_Response",
+      "location": {
+        "filename": "generated",
+        "line": 14,
+        "column": 0
+      },
+      "anonymous": false,
+      "members": [],
+      "size": 1,
+      "max_out_of_line": 0,
+      "alignment": 1,
+      "max_handles": 0
+    },
+    {
+      "name": "test.name/WithErrorSyntax_ErrorAsEnum_Response",
+      "location": {
+        "filename": "generated",
+        "line": 21,
+        "column": 0
+      },
+      "anonymous": false,
+      "members": [],
+      "size": 1,
+      "max_out_of_line": 0,
+      "alignment": 1,
+      "max_handles": 0
+    }
+  ],
   "table_declarations": [],
-  "union_declarations": [],
+  "union_declarations": [
+    {
+      "name": "test.name/WithErrorSyntax_ErrorAsPrimitive_Result",
+      "location": {
+        "filename": "generated",
+        "line": 17,
+        "column": 0
+      },
+      "maybe_attributes": [
+        {
+          "name": "Result",
+          "value": ""
+        }
+      ],
+      "members": [
+        {
+          "type": {
+            "kind": "identifier",
+            "identifier": "test.name/WithErrorSyntax_ErrorAsPrimitive_Response",
+            "nullable": false
+          },
+          "name": "response",
+          "location": {
+            "filename": "generated",
+            "line": 15,
+            "column": 0
+          },
+          "size": 1,
+          "max_out_of_line": 0,
+          "alignment": 1,
+          "offset": 4
+        },
+        {
+          "type": {
+            "kind": "primitive",
+            "subtype": "uint32"
+          },
+          "name": "err",
+          "location": {
+            "filename": "generated",
+            "line": 16,
+            "column": 0
+          },
+          "size": 4,
+          "max_out_of_line": 0,
+          "alignment": 4,
+          "offset": 4
+        }
+      ],
+      "size": 8,
+      "max_out_of_line": 0,
+      "alignment": 4,
+      "max_handles": 0
+    },
+    {
+      "name": "test.name/WithErrorSyntax_ErrorAsEnum_Result",
+      "location": {
+        "filename": "generated",
+        "line": 24,
+        "column": 0
+      },
+      "maybe_attributes": [
+        {
+          "name": "Result",
+          "value": ""
+        }
+      ],
+      "members": [
+        {
+          "type": {
+            "kind": "identifier",
+            "identifier": "test.name/WithErrorSyntax_ErrorAsEnum_Response",
+            "nullable": false
+          },
+          "name": "response",
+          "location": {
+            "filename": "generated",
+            "line": 22,
+            "column": 0
+          },
+          "size": 1,
+          "max_out_of_line": 0,
+          "alignment": 1,
+          "offset": 4
+        },
+        {
+          "type": {
+            "kind": "identifier",
+            "identifier": "test.name/ErrorEnun",
+            "nullable": false
+          },
+          "name": "err",
+          "location": {
+            "filename": "generated",
+            "line": 23,
+            "column": 0
+          },
+          "size": 4,
+          "max_out_of_line": 0,
+          "alignment": 4,
+          "offset": 4
+        }
+      ],
+      "size": 8,
+      "max_out_of_line": 0,
+      "alignment": 4,
+      "max_handles": 0
+    }
+  ],
   "xunion_declarations": [],
   "declaration_order": [
+    "test.name/WithErrorSyntax_ErrorAsPrimitive_Response",
+    "test.name/WithErrorSyntax_ErrorAsPrimitive_Result",
+    "test.name/WithErrorSyntax_ErrorAsEnum_Response",
     "test.name/KitchenSink",
     "test.name/ChannelProtocol",
     "test.name/SocketControlProtocol",
     "test.name/OvernetInternalProtocol",
-    "test.name/WithAndWithoutRequestResponse"
+    "test.name/WithAndWithoutRequestResponse",
+    "test.name/ErrorEnun",
+    "test.name/WithErrorSyntax_ErrorAsEnum_Result",
+    "test.name/WithErrorSyntax"
   ],
   "declarations": {
+    "test.name/ErrorEnun": "enum",
     "test.name/WithAndWithoutRequestResponse": "interface",
+    "test.name/WithErrorSyntax": "interface",
     "test.name/OvernetInternalProtocol": "interface",
     "test.name/SocketControlProtocol": "interface",
     "test.name/ChannelProtocol": "interface",
-    "test.name/KitchenSink": "interface"
+    "test.name/KitchenSink": "interface",
+    "test.name/WithErrorSyntax_ErrorAsPrimitive_Response": "struct",
+    "test.name/WithErrorSyntax_ErrorAsEnum_Response": "struct",
+    "test.name/WithErrorSyntax_ErrorAsPrimitive_Result": "union",
+    "test.name/WithErrorSyntax_ErrorAsEnum_Result": "union"
   }
 }
diff --git a/bin/fidlgen_dart/goldens/protocols.test.fidl.json_async.dart.golden b/bin/fidlgen_dart/goldens/protocols.test.fidl.json_async.dart.golden
index 26ed509..bf911b8 100644
--- a/bin/fidlgen_dart/goldens/protocols.test.fidl.json_async.dart.golden
+++ b/bin/fidlgen_dart/goldens/protocols.test.fidl.json_async.dart.golden
@@ -44,6 +44,289 @@
 // ignore_for_file: unnecessary_lambdas
 // ignore_for_file: comment_references
 
+class ErrorEnun extends $fidl.Enum {
+  factory ErrorEnun(int v) {
+    switch (v) {
+      case 1:
+        return errFoo;
+      case 2:
+        return errBar;
+      default:
+        return null;
+    }
+  }
+  static const ErrorEnun errFoo = const ErrorEnun._(1);
+  static const ErrorEnun errBar = const ErrorEnun._(2);
+
+  const ErrorEnun._(this.value);
+
+  @override
+  final int value;
+
+  static const Map<String, ErrorEnun> valuesMap = const {
+    r'errFoo': errFoo,
+    r'errBar': errBar,
+  };
+
+  static const List<ErrorEnun> values = const [
+    errFoo,
+    errBar,
+  ];
+
+  static ErrorEnun valueOf(String name) => valuesMap[name];
+
+  @override
+  String toString() {
+    switch (value) {
+      case 1:
+        return r'ErrorEnun.errFoo';
+      case 2:
+        return r'ErrorEnun.errBar';
+      default:
+        return null;
+    }
+  }
+
+  static ErrorEnun _ctor(int v) => new ErrorEnun(v);
+}
+
+const $fidl.EnumType<ErrorEnun> kErrorEnun_Type =
+    const $fidl.EnumType<ErrorEnun>(
+        type: const $fidl.Uint32Type(), ctor: ErrorEnun._ctor);
+
+enum WithErrorSyntaxErrorAsPrimitiveResultTag {
+  response,
+  err,
+}
+
+class WithErrorSyntaxErrorAsPrimitiveResult extends $fidl.Union {
+  const WithErrorSyntaxErrorAsPrimitiveResult.withResponse(
+      WithErrorSyntaxErrorAsPrimitiveResponse value)
+      : _data = value,
+        tag = WithErrorSyntaxErrorAsPrimitiveResultTag.response;
+
+  const WithErrorSyntaxErrorAsPrimitiveResult.withErr(int value)
+      : _data = value,
+        tag = WithErrorSyntaxErrorAsPrimitiveResultTag.err;
+
+  WithErrorSyntaxErrorAsPrimitiveResult._(this.tag, Object data) : _data = data;
+
+  final WithErrorSyntaxErrorAsPrimitiveResultTag tag;
+  final _data;
+  WithErrorSyntaxErrorAsPrimitiveResponse get response {
+    if (tag != WithErrorSyntaxErrorAsPrimitiveResultTag.response) {
+      return null;
+    }
+    return _data;
+  }
+
+  int get err {
+    if (tag != WithErrorSyntaxErrorAsPrimitiveResultTag.err) {
+      return null;
+    }
+    return _data;
+  }
+
+  @override
+  String toString() {
+    switch (tag) {
+      case WithErrorSyntaxErrorAsPrimitiveResultTag.response:
+        return 'WithErrorSyntaxErrorAsPrimitiveResult.response($response)';
+      case WithErrorSyntaxErrorAsPrimitiveResultTag.err:
+        return 'WithErrorSyntaxErrorAsPrimitiveResult.err($err)';
+      default:
+        return null;
+    }
+  }
+
+  @override
+  int get $index => tag.index;
+
+  @override
+  Object get $data => _data;
+
+  static WithErrorSyntaxErrorAsPrimitiveResult _ctor(int index, Object data) {
+    return new WithErrorSyntaxErrorAsPrimitiveResult._(
+        WithErrorSyntaxErrorAsPrimitiveResultTag.values[index], data);
+  }
+}
+
+// See FIDL-308:
+// ignore: recursive_compile_time_constant
+const $fidl.UnionType<WithErrorSyntaxErrorAsPrimitiveResult>
+    kWithErrorSyntax_ErrorAsPrimitive_Result_Type =
+    const $fidl.UnionType<WithErrorSyntaxErrorAsPrimitiveResult>(
+  encodedSize: 8,
+  members: const <$fidl.MemberType>[
+    const $fidl.MemberType<WithErrorSyntaxErrorAsPrimitiveResponse>(
+        type: kWithErrorSyntax_ErrorAsPrimitive_Response_Type, offset: 4),
+    const $fidl.MemberType<int>(type: const $fidl.Uint32Type(), offset: 4),
+  ],
+  ctor: WithErrorSyntaxErrorAsPrimitiveResult._ctor,
+);
+
+enum WithErrorSyntaxErrorAsEnumResultTag {
+  response,
+  err,
+}
+
+class WithErrorSyntaxErrorAsEnumResult extends $fidl.Union {
+  const WithErrorSyntaxErrorAsEnumResult.withResponse(
+      WithErrorSyntaxErrorAsEnumResponse value)
+      : _data = value,
+        tag = WithErrorSyntaxErrorAsEnumResultTag.response;
+
+  const WithErrorSyntaxErrorAsEnumResult.withErr(ErrorEnun value)
+      : _data = value,
+        tag = WithErrorSyntaxErrorAsEnumResultTag.err;
+
+  WithErrorSyntaxErrorAsEnumResult._(this.tag, Object data) : _data = data;
+
+  final WithErrorSyntaxErrorAsEnumResultTag tag;
+  final _data;
+  WithErrorSyntaxErrorAsEnumResponse get response {
+    if (tag != WithErrorSyntaxErrorAsEnumResultTag.response) {
+      return null;
+    }
+    return _data;
+  }
+
+  ErrorEnun get err {
+    if (tag != WithErrorSyntaxErrorAsEnumResultTag.err) {
+      return null;
+    }
+    return _data;
+  }
+
+  @override
+  String toString() {
+    switch (tag) {
+      case WithErrorSyntaxErrorAsEnumResultTag.response:
+        return 'WithErrorSyntaxErrorAsEnumResult.response($response)';
+      case WithErrorSyntaxErrorAsEnumResultTag.err:
+        return 'WithErrorSyntaxErrorAsEnumResult.err($err)';
+      default:
+        return null;
+    }
+  }
+
+  @override
+  int get $index => tag.index;
+
+  @override
+  Object get $data => _data;
+
+  static WithErrorSyntaxErrorAsEnumResult _ctor(int index, Object data) {
+    return new WithErrorSyntaxErrorAsEnumResult._(
+        WithErrorSyntaxErrorAsEnumResultTag.values[index], data);
+  }
+}
+
+// See FIDL-308:
+// ignore: recursive_compile_time_constant
+const $fidl.UnionType<WithErrorSyntaxErrorAsEnumResult>
+    kWithErrorSyntax_ErrorAsEnum_Result_Type =
+    const $fidl.UnionType<WithErrorSyntaxErrorAsEnumResult>(
+  encodedSize: 8,
+  members: const <$fidl.MemberType>[
+    const $fidl.MemberType<WithErrorSyntaxErrorAsEnumResponse>(
+        type: kWithErrorSyntax_ErrorAsEnum_Response_Type, offset: 4),
+    const $fidl.MemberType<ErrorEnun>(type: kErrorEnun_Type, offset: 4),
+  ],
+  ctor: WithErrorSyntaxErrorAsEnumResult._ctor,
+);
+
+class WithErrorSyntaxErrorAsPrimitiveResponse extends $fidl.Struct {
+  const WithErrorSyntaxErrorAsPrimitiveResponse({
+    this.reserved: 0,
+  });
+  WithErrorSyntaxErrorAsPrimitiveResponse.clone(
+    WithErrorSyntaxErrorAsPrimitiveResponse $orig, {
+    int reserved,
+  }) : this(
+          reserved: reserved ?? $orig.reserved,
+        );
+
+  WithErrorSyntaxErrorAsPrimitiveResponse._(List<Object> argv)
+      : reserved = argv[0];
+  final int reserved;
+
+  @override
+  List<Object> get $fields {
+    return <Object>[
+      reserved,
+    ];
+  }
+
+  @override
+  String toString() {
+    // ignore: prefer_interpolation_to_compose_strings
+    return r'WithErrorSyntaxErrorAsPrimitiveResponse' r'(reserved: ' +
+        reserved.toString() +
+        r')';
+  }
+
+  static WithErrorSyntaxErrorAsPrimitiveResponse _ctor(List<Object> argv) =>
+      new WithErrorSyntaxErrorAsPrimitiveResponse._(argv);
+}
+
+// See FIDL-308:
+// ignore: recursive_compile_time_constant
+const $fidl.StructType<WithErrorSyntaxErrorAsPrimitiveResponse>
+    kWithErrorSyntax_ErrorAsPrimitive_Response_Type =
+    const $fidl.StructType<WithErrorSyntaxErrorAsPrimitiveResponse>(
+  encodedSize: 1,
+  members: const <$fidl.MemberType>[
+    const $fidl.MemberType<int>(type: const $fidl.Uint8Type(), offset: 0),
+  ],
+  ctor: WithErrorSyntaxErrorAsPrimitiveResponse._ctor,
+);
+
+class WithErrorSyntaxErrorAsEnumResponse extends $fidl.Struct {
+  const WithErrorSyntaxErrorAsEnumResponse({
+    this.reserved: 0,
+  });
+  WithErrorSyntaxErrorAsEnumResponse.clone(
+    WithErrorSyntaxErrorAsEnumResponse $orig, {
+    int reserved,
+  }) : this(
+          reserved: reserved ?? $orig.reserved,
+        );
+
+  WithErrorSyntaxErrorAsEnumResponse._(List<Object> argv) : reserved = argv[0];
+  final int reserved;
+
+  @override
+  List<Object> get $fields {
+    return <Object>[
+      reserved,
+    ];
+  }
+
+  @override
+  String toString() {
+    // ignore: prefer_interpolation_to_compose_strings
+    return r'WithErrorSyntaxErrorAsEnumResponse' r'(reserved: ' +
+        reserved.toString() +
+        r')';
+  }
+
+  static WithErrorSyntaxErrorAsEnumResponse _ctor(List<Object> argv) =>
+      new WithErrorSyntaxErrorAsEnumResponse._(argv);
+}
+
+// See FIDL-308:
+// ignore: recursive_compile_time_constant
+const $fidl.StructType<WithErrorSyntaxErrorAsEnumResponse>
+    kWithErrorSyntax_ErrorAsEnum_Response_Type =
+    const $fidl.StructType<WithErrorSyntaxErrorAsEnumResponse>(
+  encodedSize: 1,
+  members: const <$fidl.MemberType>[
+    const $fidl.MemberType<int>(type: const $fidl.Uint8Type(), offset: 0),
+  ],
+  ctor: WithErrorSyntaxErrorAsEnumResponse._ctor,
+);
+
 // ignore: unused_element, avoid_private_typedef_functions
 typedef _VoidCallback = void Function();
 
@@ -355,7 +638,9 @@
   Future<void> noRequestNoResponse() async {
     if (!ctrl.isBound) {
       return new Future.error(
-          new $fidl.FidlStateException('The proxy is closed.'));
+          new $fidl.FidlStateException(
+              'Proxy<${ctrl.$interfaceName}> is closed.'),
+          StackTrace.current);
     }
 
     final $fidl.Encoder $encoder = new $fidl.Encoder();
@@ -370,7 +655,9 @@
   Future<void> noRequestEmptyResponse() async {
     if (!ctrl.isBound) {
       return new Future.error(
-          new $fidl.FidlStateException('The proxy is closed.'));
+          new $fidl.FidlStateException(
+              'Proxy<${ctrl.$interfaceName}> is closed.'),
+          StackTrace.current);
     }
 
     final $fidl.Encoder $encoder = new $fidl.Encoder();
@@ -385,7 +672,9 @@
   Future<String> noRequestWithResponse() async {
     if (!ctrl.isBound) {
       return new Future.error(
-          new $fidl.FidlStateException('The proxy is closed.'));
+          new $fidl.FidlStateException(
+              'Proxy<${ctrl.$interfaceName}> is closed.'),
+          StackTrace.current);
     }
 
     final $fidl.Encoder $encoder = new $fidl.Encoder();
@@ -400,7 +689,9 @@
   Future<void> withRequestNoResponse(String arg) async {
     if (!ctrl.isBound) {
       return new Future.error(
-          new $fidl.FidlStateException('The proxy is closed.'));
+          new $fidl.FidlStateException(
+              'Proxy<${ctrl.$interfaceName}> is closed.'),
+          StackTrace.current);
     }
 
     final $fidl.Encoder $encoder = new $fidl.Encoder();
@@ -419,7 +710,9 @@
   Future<void> withRequestEmptyResponse(String arg) async {
     if (!ctrl.isBound) {
       return new Future.error(
-          new $fidl.FidlStateException('The proxy is closed.'));
+          new $fidl.FidlStateException(
+              'Proxy<${ctrl.$interfaceName}> is closed.'),
+          StackTrace.current);
     }
 
     final $fidl.Encoder $encoder = new $fidl.Encoder();
@@ -438,7 +731,9 @@
   Future<String> withRequestWithResponse(String arg) async {
     if (!ctrl.isBound) {
       return new Future.error(
-          new $fidl.FidlStateException('The proxy is closed.'));
+          new $fidl.FidlStateException(
+              'Proxy<${ctrl.$interfaceName}> is closed.'),
+          StackTrace.current);
     }
 
     final $fidl.Encoder $encoder = new $fidl.Encoder();
@@ -687,6 +982,243 @@
   }
 }
 
+// errorAsPrimitive: () -> (WithErrorSyntaxErrorAsPrimitiveResult result)
+const int _kWithErrorSyntax_ErrorAsPrimitive_Ordinal = 2069369145;
+const $fidl.MethodType _kWithErrorSyntax_ErrorAsPrimitive_Type =
+    const $fidl.MethodType(
+  request: null,
+  response: const <$fidl.MemberType>[
+    const $fidl.MemberType<WithErrorSyntaxErrorAsPrimitiveResult>(
+        type: kWithErrorSyntax_ErrorAsPrimitive_Result_Type, offset: 16),
+  ],
+  name: r"WithErrorSyntax.ErrorAsPrimitive",
+);
+// errorAsEnum: () -> (WithErrorSyntaxErrorAsEnumResult result)
+const int _kWithErrorSyntax_ErrorAsEnum_Ordinal = 1284890143;
+const $fidl.MethodType _kWithErrorSyntax_ErrorAsEnum_Type =
+    const $fidl.MethodType(
+  request: null,
+  response: const <$fidl.MemberType>[
+    const $fidl.MemberType<WithErrorSyntaxErrorAsEnumResult>(
+        type: kWithErrorSyntax_ErrorAsEnum_Result_Type, offset: 16),
+  ],
+  name: r"WithErrorSyntax.ErrorAsEnum",
+);
+
+abstract class WithErrorSyntax extends $fidl.Service {
+  static const String $serviceName = null;
+  @override
+  $fidl.ServiceData get $serviceData => const WithErrorSyntaxData();
+  Future<WithErrorSyntaxErrorAsPrimitiveResult> errorAsPrimitive();
+  Future<WithErrorSyntaxErrorAsEnumResult> errorAsEnum();
+}
+
+class WithErrorSyntaxData implements $fidl.ServiceData<WithErrorSyntax> {
+  const WithErrorSyntaxData();
+
+  @override
+  String getName() {
+    return WithErrorSyntax.$serviceName;
+  }
+
+  @override
+  $fidl.AsyncBinding getBinding() {
+    return WithErrorSyntaxBinding();
+  }
+}
+
+class WithErrorSyntaxProxy extends $fidl.AsyncProxy<WithErrorSyntax>
+    implements WithErrorSyntax {
+  WithErrorSyntaxProxy()
+      : super(new $fidl.AsyncProxyController<WithErrorSyntax>(
+            $serviceName: null, $interfaceName: r'WithErrorSyntax')) {
+    ctrl.onResponse = _handleResponse;
+  }
+
+  @override
+  $fidl.ServiceData get $serviceData => WithErrorSyntaxData();
+
+  void _handleEvent($fidl.Message $message) {
+    final $fidl.Decoder $decoder = new $fidl.Decoder($message);
+    switch ($message.ordinal) {
+      default:
+        ctrl.proxyError(new $fidl.FidlError(
+            'Unexpected message ordinal: ${$message.ordinal}'));
+        ctrl.close();
+        break;
+    }
+  }
+
+  void _handleResponse($fidl.Message $message) {
+    final int $txid = $message.txid;
+    if ($txid == 0) {
+      _handleEvent($message);
+      return;
+    }
+    final Completer $completer = ctrl.getCompleter($txid);
+    if ($completer == null) {
+      $message.closeHandles();
+      return;
+    }
+    final $fidl.Decoder $decoder = new $fidl.Decoder($message);
+    switch ($message.ordinal) {
+      case _kWithErrorSyntax_ErrorAsPrimitive_Ordinal:
+        final String _name = _kWithErrorSyntax_ErrorAsPrimitive_Type.name;
+        try {
+          Timeline.startSync(_name);
+          final List<$fidl.MemberType> $types =
+              _kWithErrorSyntax_ErrorAsPrimitive_Type.response;
+          $decoder.claimMemory(24);
+          $completer.complete(
+            $types[0].decode($decoder, 0),
+          );
+          // ignore: avoid_catches_without_on_clauses
+        } catch (_e) {
+          ctrl.proxyError(new $fidl.FidlError(
+              'Exception handling method response $_name: $_e'));
+          ctrl.close();
+          rethrow;
+        } finally {
+          Timeline.finishSync();
+        }
+        break;
+      case _kWithErrorSyntax_ErrorAsEnum_Ordinal:
+        final String _name = _kWithErrorSyntax_ErrorAsEnum_Type.name;
+        try {
+          Timeline.startSync(_name);
+          final List<$fidl.MemberType> $types =
+              _kWithErrorSyntax_ErrorAsEnum_Type.response;
+          $decoder.claimMemory(24);
+          $completer.complete(
+            $types[0].decode($decoder, 0),
+          );
+          // ignore: avoid_catches_without_on_clauses
+        } catch (_e) {
+          ctrl.proxyError(new $fidl.FidlError(
+              'Exception handling method response $_name: $_e'));
+          ctrl.close();
+          rethrow;
+        } finally {
+          Timeline.finishSync();
+        }
+        break;
+      default:
+        ctrl.proxyError(new $fidl.FidlError(
+            'Unexpected message ordinal: ${$message.ordinal}'));
+        ctrl.close();
+        break;
+    }
+  }
+
+  @override
+  Future<WithErrorSyntaxErrorAsPrimitiveResult> errorAsPrimitive() async {
+    if (!ctrl.isBound) {
+      return new Future.error(
+          new $fidl.FidlStateException(
+              'Proxy<${ctrl.$interfaceName}> is closed.'),
+          StackTrace.current);
+    }
+
+    final $fidl.Encoder $encoder = new $fidl.Encoder();
+    $encoder.encodeMessageHeader(_kWithErrorSyntax_ErrorAsPrimitive_Ordinal, 0);
+    final $completer = new Completer<WithErrorSyntaxErrorAsPrimitiveResult>();
+    ctrl.sendMessageWithResponse($encoder.message, $completer);
+    return $completer.future;
+  }
+
+  @override
+  Future<WithErrorSyntaxErrorAsEnumResult> errorAsEnum() async {
+    if (!ctrl.isBound) {
+      return new Future.error(
+          new $fidl.FidlStateException(
+              'Proxy<${ctrl.$interfaceName}> is closed.'),
+          StackTrace.current);
+    }
+
+    final $fidl.Encoder $encoder = new $fidl.Encoder();
+    $encoder.encodeMessageHeader(_kWithErrorSyntax_ErrorAsEnum_Ordinal, 0);
+    final $completer = new Completer<WithErrorSyntaxErrorAsEnumResult>();
+    ctrl.sendMessageWithResponse($encoder.message, $completer);
+    return $completer.future;
+  }
+}
+
+class WithErrorSyntaxBinding extends $fidl.AsyncBinding<WithErrorSyntax> {
+  WithErrorSyntaxBinding() : super(r"WithErrorSyntax");
+
+  @override
+  void handleMessage($fidl.Message $message, $fidl.MessageSink $respond) {
+    final $fidl.Decoder $decoder = new $fidl.Decoder($message);
+    switch ($message.ordinal) {
+      case _kWithErrorSyntax_ErrorAsPrimitive_Ordinal:
+        final String _name = _kWithErrorSyntax_ErrorAsPrimitive_Type.name;
+        try {
+          Timeline.startSync(_name);
+          final List<$fidl.MemberType> $types =
+              _kWithErrorSyntax_ErrorAsPrimitive_Type.request;
+          $decoder.claimMemory(16);
+          final Future<WithErrorSyntaxErrorAsPrimitiveResult> $future =
+              impl.errorAsPrimitive();
+          $future.then(($response) {
+            final $fidl.Encoder $encoder = new $fidl.Encoder();
+            $encoder.encodeMessageHeader(
+                _kWithErrorSyntax_ErrorAsPrimitive_Ordinal, $message.txid);
+            $encoder.alloc(24 - $fidl.kMessageHeaderSize);
+            final List<$fidl.MemberType> $types =
+                _kWithErrorSyntax_ErrorAsPrimitive_Type.response;
+            $types[0].encode($encoder, $response, 0);
+            $respond($encoder.message);
+          }, onError: (_e) {
+            close();
+            print('Exception handling method call $_name: $_e');
+          });
+          // ignore: avoid_catches_without_on_clauses
+        } catch (_e) {
+          close();
+          print('Exception handling method call $_name: $_e');
+          rethrow;
+        } finally {
+          Timeline.finishSync();
+        }
+        break;
+      case _kWithErrorSyntax_ErrorAsEnum_Ordinal:
+        final String _name = _kWithErrorSyntax_ErrorAsEnum_Type.name;
+        try {
+          Timeline.startSync(_name);
+          final List<$fidl.MemberType> $types =
+              _kWithErrorSyntax_ErrorAsEnum_Type.request;
+          $decoder.claimMemory(16);
+          final Future<WithErrorSyntaxErrorAsEnumResult> $future =
+              impl.errorAsEnum();
+          $future.then(($response) {
+            final $fidl.Encoder $encoder = new $fidl.Encoder();
+            $encoder.encodeMessageHeader(
+                _kWithErrorSyntax_ErrorAsEnum_Ordinal, $message.txid);
+            $encoder.alloc(24 - $fidl.kMessageHeaderSize);
+            final List<$fidl.MemberType> $types =
+                _kWithErrorSyntax_ErrorAsEnum_Type.response;
+            $types[0].encode($encoder, $response, 0);
+            $respond($encoder.message);
+          }, onError: (_e) {
+            close();
+            print('Exception handling method call $_name: $_e');
+          });
+          // ignore: avoid_catches_without_on_clauses
+        } catch (_e) {
+          close();
+          print('Exception handling method call $_name: $_e');
+          rethrow;
+        } finally {
+          Timeline.finishSync();
+        }
+        break;
+      default:
+        throw new $fidl.FidlError(
+            'Unexpected message name for WithErrorSyntaxBinding');
+    }
+  }
+}
+
 // methodA: (int a, int b)
 const int _kOvernetInternalProtocol_MethodA_Ordinal = 1993818253;
 const $fidl.MethodType _kOvernetInternalProtocol_MethodA_Type =
@@ -848,7 +1380,9 @@
   Future<void> methodA(int a, int b) async {
     if (!ctrl.isBound) {
       return new Future.error(
-          new $fidl.FidlStateException('The proxy is closed.'));
+          new $fidl.FidlStateException(
+              'Proxy<${ctrl.$interfaceName}> is closed.'),
+          StackTrace.current);
     }
 
     final $fidl.Encoder $encoder = new $fidl.Encoder();
@@ -873,7 +1407,9 @@
   Future<int> methodB(int a, int b) async {
     if (!ctrl.isBound) {
       return new Future.error(
-          new $fidl.FidlStateException('The proxy is closed.'));
+          new $fidl.FidlStateException(
+              'Proxy<${ctrl.$interfaceName}> is closed.'),
+          StackTrace.current);
     }
 
     final $fidl.Encoder $encoder = new $fidl.Encoder();
@@ -1144,7 +1680,9 @@
   Future<void> methodA(int a, int b) async {
     if (!ctrl.isBound) {
       return new Future.error(
-          new $fidl.FidlStateException('The proxy is closed.'));
+          new $fidl.FidlStateException(
+              'Proxy<${ctrl.$interfaceName}> is closed.'),
+          StackTrace.current);
     }
 
     final $fidl.Encoder $encoder = new $fidl.Encoder();
@@ -1169,7 +1707,9 @@
   Future<int> methodB(int a, int b) async {
     if (!ctrl.isBound) {
       return new Future.error(
-          new $fidl.FidlStateException('The proxy is closed.'));
+          new $fidl.FidlStateException(
+              'Proxy<${ctrl.$interfaceName}> is closed.'),
+          StackTrace.current);
     }
 
     final $fidl.Encoder $encoder = new $fidl.Encoder();
@@ -1435,7 +1975,9 @@
   Future<void> methodA(int a, int b) async {
     if (!ctrl.isBound) {
       return new Future.error(
-          new $fidl.FidlStateException('The proxy is closed.'));
+          new $fidl.FidlStateException(
+              'Proxy<${ctrl.$interfaceName}> is closed.'),
+          StackTrace.current);
     }
 
     final $fidl.Encoder $encoder = new $fidl.Encoder();
@@ -1460,7 +2002,9 @@
   Future<int> methodB(int a, int b) async {
     if (!ctrl.isBound) {
       return new Future.error(
-          new $fidl.FidlStateException('The proxy is closed.'));
+          new $fidl.FidlStateException(
+              'Proxy<${ctrl.$interfaceName}> is closed.'),
+          StackTrace.current);
     }
 
     final $fidl.Encoder $encoder = new $fidl.Encoder();
@@ -1724,7 +2268,9 @@
   Future<void> methodA(int a, int b) async {
     if (!ctrl.isBound) {
       return new Future.error(
-          new $fidl.FidlStateException('The proxy is closed.'));
+          new $fidl.FidlStateException(
+              'Proxy<${ctrl.$interfaceName}> is closed.'),
+          StackTrace.current);
     }
 
     final $fidl.Encoder $encoder = new $fidl.Encoder();
@@ -1748,7 +2294,9 @@
   Future<int> methodB(int a, int b) async {
     if (!ctrl.isBound) {
       return new Future.error(
-          new $fidl.FidlStateException('The proxy is closed.'));
+          new $fidl.FidlStateException(
+              'Proxy<${ctrl.$interfaceName}> is closed.'),
+          StackTrace.current);
     }
 
     final $fidl.Encoder $encoder = new $fidl.Encoder();
diff --git a/bin/fidlgen_dart/goldens/protocols.test.fidl.json_sync.dart.golden b/bin/fidlgen_dart/goldens/protocols.test.fidl.json_sync.dart.golden
index fc8c22c..1a98113 100644
--- a/bin/fidlgen_dart/goldens/protocols.test.fidl.json_sync.dart.golden
+++ b/bin/fidlgen_dart/goldens/protocols.test.fidl.json_sync.dart.golden
@@ -40,6 +40,289 @@
 // ignore_for_file: prefer_generic_function_type_aliases
 // ignore_for_file: prefer_equal_for_default_values
 
+class ErrorEnun extends $fidl.Enum {
+  factory ErrorEnun(int v) {
+    switch (v) {
+      case 1:
+        return errFoo;
+      case 2:
+        return errBar;
+      default:
+        return null;
+    }
+  }
+  static const ErrorEnun errFoo = const ErrorEnun._(1);
+  static const ErrorEnun errBar = const ErrorEnun._(2);
+
+  const ErrorEnun._(this.value);
+
+  @override
+  final int value;
+
+  static const Map<String, ErrorEnun> valuesMap = const {
+    r'errFoo': errFoo,
+    r'errBar': errBar,
+  };
+
+  static const List<ErrorEnun> values = const [
+    errFoo,
+    errBar,
+  ];
+
+  static ErrorEnun valueOf(String name) => valuesMap[name];
+
+  @override
+  String toString() {
+    switch (value) {
+      case 1:
+        return r'ErrorEnun.errFoo';
+      case 2:
+        return r'ErrorEnun.errBar';
+      default:
+        return null;
+    }
+  }
+
+  static ErrorEnun _ctor(int v) => new ErrorEnun(v);
+}
+
+const $fidl.EnumType<ErrorEnun> kErrorEnun_Type =
+    const $fidl.EnumType<ErrorEnun>(
+        type: const $fidl.Uint32Type(), ctor: ErrorEnun._ctor);
+
+enum WithErrorSyntaxErrorAsPrimitiveResultTag {
+  response,
+  err,
+}
+
+class WithErrorSyntaxErrorAsPrimitiveResult extends $fidl.Union {
+  const WithErrorSyntaxErrorAsPrimitiveResult.withResponse(
+      WithErrorSyntaxErrorAsPrimitiveResponse value)
+      : _data = value,
+        tag = WithErrorSyntaxErrorAsPrimitiveResultTag.response;
+
+  const WithErrorSyntaxErrorAsPrimitiveResult.withErr(int value)
+      : _data = value,
+        tag = WithErrorSyntaxErrorAsPrimitiveResultTag.err;
+
+  WithErrorSyntaxErrorAsPrimitiveResult._(this.tag, Object data) : _data = data;
+
+  final WithErrorSyntaxErrorAsPrimitiveResultTag tag;
+  final _data;
+  WithErrorSyntaxErrorAsPrimitiveResponse get response {
+    if (tag != WithErrorSyntaxErrorAsPrimitiveResultTag.response) {
+      return null;
+    }
+    return _data;
+  }
+
+  int get err {
+    if (tag != WithErrorSyntaxErrorAsPrimitiveResultTag.err) {
+      return null;
+    }
+    return _data;
+  }
+
+  @override
+  String toString() {
+    switch (tag) {
+      case WithErrorSyntaxErrorAsPrimitiveResultTag.response:
+        return 'WithErrorSyntaxErrorAsPrimitiveResult.response($response)';
+      case WithErrorSyntaxErrorAsPrimitiveResultTag.err:
+        return 'WithErrorSyntaxErrorAsPrimitiveResult.err($err)';
+      default:
+        return null;
+    }
+  }
+
+  @override
+  int get $index => tag.index;
+
+  @override
+  Object get $data => _data;
+
+  static WithErrorSyntaxErrorAsPrimitiveResult _ctor(int index, Object data) {
+    return new WithErrorSyntaxErrorAsPrimitiveResult._(
+        WithErrorSyntaxErrorAsPrimitiveResultTag.values[index], data);
+  }
+}
+
+// See FIDL-308:
+// ignore: recursive_compile_time_constant
+const $fidl.UnionType<WithErrorSyntaxErrorAsPrimitiveResult>
+    kWithErrorSyntax_ErrorAsPrimitive_Result_Type =
+    const $fidl.UnionType<WithErrorSyntaxErrorAsPrimitiveResult>(
+  encodedSize: 8,
+  members: const <$fidl.MemberType>[
+    const $fidl.MemberType<WithErrorSyntaxErrorAsPrimitiveResponse>(
+        type: kWithErrorSyntax_ErrorAsPrimitive_Response_Type, offset: 4),
+    const $fidl.MemberType<int>(type: const $fidl.Uint32Type(), offset: 4),
+  ],
+  ctor: WithErrorSyntaxErrorAsPrimitiveResult._ctor,
+);
+
+enum WithErrorSyntaxErrorAsEnumResultTag {
+  response,
+  err,
+}
+
+class WithErrorSyntaxErrorAsEnumResult extends $fidl.Union {
+  const WithErrorSyntaxErrorAsEnumResult.withResponse(
+      WithErrorSyntaxErrorAsEnumResponse value)
+      : _data = value,
+        tag = WithErrorSyntaxErrorAsEnumResultTag.response;
+
+  const WithErrorSyntaxErrorAsEnumResult.withErr(ErrorEnun value)
+      : _data = value,
+        tag = WithErrorSyntaxErrorAsEnumResultTag.err;
+
+  WithErrorSyntaxErrorAsEnumResult._(this.tag, Object data) : _data = data;
+
+  final WithErrorSyntaxErrorAsEnumResultTag tag;
+  final _data;
+  WithErrorSyntaxErrorAsEnumResponse get response {
+    if (tag != WithErrorSyntaxErrorAsEnumResultTag.response) {
+      return null;
+    }
+    return _data;
+  }
+
+  ErrorEnun get err {
+    if (tag != WithErrorSyntaxErrorAsEnumResultTag.err) {
+      return null;
+    }
+    return _data;
+  }
+
+  @override
+  String toString() {
+    switch (tag) {
+      case WithErrorSyntaxErrorAsEnumResultTag.response:
+        return 'WithErrorSyntaxErrorAsEnumResult.response($response)';
+      case WithErrorSyntaxErrorAsEnumResultTag.err:
+        return 'WithErrorSyntaxErrorAsEnumResult.err($err)';
+      default:
+        return null;
+    }
+  }
+
+  @override
+  int get $index => tag.index;
+
+  @override
+  Object get $data => _data;
+
+  static WithErrorSyntaxErrorAsEnumResult _ctor(int index, Object data) {
+    return new WithErrorSyntaxErrorAsEnumResult._(
+        WithErrorSyntaxErrorAsEnumResultTag.values[index], data);
+  }
+}
+
+// See FIDL-308:
+// ignore: recursive_compile_time_constant
+const $fidl.UnionType<WithErrorSyntaxErrorAsEnumResult>
+    kWithErrorSyntax_ErrorAsEnum_Result_Type =
+    const $fidl.UnionType<WithErrorSyntaxErrorAsEnumResult>(
+  encodedSize: 8,
+  members: const <$fidl.MemberType>[
+    const $fidl.MemberType<WithErrorSyntaxErrorAsEnumResponse>(
+        type: kWithErrorSyntax_ErrorAsEnum_Response_Type, offset: 4),
+    const $fidl.MemberType<ErrorEnun>(type: kErrorEnun_Type, offset: 4),
+  ],
+  ctor: WithErrorSyntaxErrorAsEnumResult._ctor,
+);
+
+class WithErrorSyntaxErrorAsPrimitiveResponse extends $fidl.Struct {
+  const WithErrorSyntaxErrorAsPrimitiveResponse({
+    this.reserved: 0,
+  });
+  WithErrorSyntaxErrorAsPrimitiveResponse.clone(
+    WithErrorSyntaxErrorAsPrimitiveResponse $orig, {
+    int reserved,
+  }) : this(
+          reserved: reserved ?? $orig.reserved,
+        );
+
+  WithErrorSyntaxErrorAsPrimitiveResponse._(List<Object> argv)
+      : reserved = argv[0];
+  final int reserved;
+
+  @override
+  List<Object> get $fields {
+    return <Object>[
+      reserved,
+    ];
+  }
+
+  @override
+  String toString() {
+    // ignore: prefer_interpolation_to_compose_strings
+    return r'WithErrorSyntaxErrorAsPrimitiveResponse' r'(reserved: ' +
+        reserved.toString() +
+        r')';
+  }
+
+  static WithErrorSyntaxErrorAsPrimitiveResponse _ctor(List<Object> argv) =>
+      new WithErrorSyntaxErrorAsPrimitiveResponse._(argv);
+}
+
+// See FIDL-308:
+// ignore: recursive_compile_time_constant
+const $fidl.StructType<WithErrorSyntaxErrorAsPrimitiveResponse>
+    kWithErrorSyntax_ErrorAsPrimitive_Response_Type =
+    const $fidl.StructType<WithErrorSyntaxErrorAsPrimitiveResponse>(
+  encodedSize: 1,
+  members: const <$fidl.MemberType>[
+    const $fidl.MemberType<int>(type: const $fidl.Uint8Type(), offset: 0),
+  ],
+  ctor: WithErrorSyntaxErrorAsPrimitiveResponse._ctor,
+);
+
+class WithErrorSyntaxErrorAsEnumResponse extends $fidl.Struct {
+  const WithErrorSyntaxErrorAsEnumResponse({
+    this.reserved: 0,
+  });
+  WithErrorSyntaxErrorAsEnumResponse.clone(
+    WithErrorSyntaxErrorAsEnumResponse $orig, {
+    int reserved,
+  }) : this(
+          reserved: reserved ?? $orig.reserved,
+        );
+
+  WithErrorSyntaxErrorAsEnumResponse._(List<Object> argv) : reserved = argv[0];
+  final int reserved;
+
+  @override
+  List<Object> get $fields {
+    return <Object>[
+      reserved,
+    ];
+  }
+
+  @override
+  String toString() {
+    // ignore: prefer_interpolation_to_compose_strings
+    return r'WithErrorSyntaxErrorAsEnumResponse' r'(reserved: ' +
+        reserved.toString() +
+        r')';
+  }
+
+  static WithErrorSyntaxErrorAsEnumResponse _ctor(List<Object> argv) =>
+      new WithErrorSyntaxErrorAsEnumResponse._(argv);
+}
+
+// See FIDL-308:
+// ignore: recursive_compile_time_constant
+const $fidl.StructType<WithErrorSyntaxErrorAsEnumResponse>
+    kWithErrorSyntax_ErrorAsEnum_Response_Type =
+    const $fidl.StructType<WithErrorSyntaxErrorAsEnumResponse>(
+  encodedSize: 1,
+  members: const <$fidl.MemberType>[
+    const $fidl.MemberType<int>(type: const $fidl.Uint8Type(), offset: 0),
+  ],
+  ctor: WithErrorSyntaxErrorAsEnumResponse._ctor,
+);
+
 abstract class WithAndWithoutRequestResponse {
   static const String $serviceName = null;
   void noRequestNoResponse();
@@ -673,6 +956,240 @@
   }
 }
 
+abstract class WithErrorSyntax {
+  static const String $serviceName = null;
+  void errorAsPrimitive(
+      void callback(WithErrorSyntaxErrorAsPrimitiveResult result));
+  void errorAsEnum(void callback(WithErrorSyntaxErrorAsEnumResult result));
+}
+
+// errorAsPrimitive: () -> (WithErrorSyntaxErrorAsPrimitiveResult result)
+const int _kWithErrorSyntax_ErrorAsPrimitive_Ordinal = 2069369145;
+const $fidl.MethodType _kWithErrorSyntax_ErrorAsPrimitive_Type =
+    const $fidl.MethodType(
+  request: null,
+  response: const <$fidl.MemberType>[
+    const $fidl.MemberType<WithErrorSyntaxErrorAsPrimitiveResult>(
+        type: kWithErrorSyntax_ErrorAsPrimitive_Result_Type, offset: 16),
+  ],
+  name: r"WithErrorSyntax.ErrorAsPrimitive",
+);
+// errorAsEnum: () -> (WithErrorSyntaxErrorAsEnumResult result)
+const int _kWithErrorSyntax_ErrorAsEnum_Ordinal = 1284890143;
+const $fidl.MethodType _kWithErrorSyntax_ErrorAsEnum_Type =
+    const $fidl.MethodType(
+  request: null,
+  response: const <$fidl.MemberType>[
+    const $fidl.MemberType<WithErrorSyntaxErrorAsEnumResult>(
+        type: kWithErrorSyntax_ErrorAsEnum_Result_Type, offset: 16),
+  ],
+  name: r"WithErrorSyntax.ErrorAsEnum",
+);
+
+class WithErrorSyntaxProxy extends $fidl.Proxy<WithErrorSyntax>
+    implements WithErrorSyntax {
+  WithErrorSyntaxProxy()
+      : super(new $fidl.ProxyController<WithErrorSyntax>(
+            $serviceName: null, $interfaceName: r'WithErrorSyntax')) {
+    ctrl.onResponse = _handleResponse;
+  }
+
+  void _handleEvent($fidl.Message $message) {
+    final $fidl.Decoder $decoder = new $fidl.Decoder($message);
+    switch ($message.ordinal) {
+      default:
+        ctrl.proxyError('Unexpected message ordinal: ${$message.ordinal}');
+        ctrl.close();
+        break;
+    }
+  }
+
+  void _handleResponse($fidl.Message $message) {
+    final int $txid = $message.txid;
+    if ($txid == 0) {
+      _handleEvent($message);
+      return;
+    }
+    final Function $callback = ctrl.getCallback($txid);
+    if ($callback == null) {
+      $message.closeHandles();
+      return;
+    }
+    final $fidl.Decoder $decoder = new $fidl.Decoder($message);
+    switch ($message.ordinal) {
+      case _kWithErrorSyntax_ErrorAsPrimitive_Ordinal:
+        final String _name = _kWithErrorSyntax_ErrorAsPrimitive_Type.name;
+        try {
+          Timeline.startSync(_name);
+          final List<$fidl.MemberType> $types =
+              _kWithErrorSyntax_ErrorAsPrimitive_Type.response;
+          $decoder.claimMemory(24);
+          $callback(
+            $types[0].decode($decoder, 0),
+          );
+          // ignore: avoid_catches_without_on_clauses
+        } catch (_e) {
+          ctrl.proxyError('Exception handling method response $_name: $_e');
+          ctrl.close();
+          rethrow;
+        } finally {
+          Timeline.finishSync();
+        }
+        break;
+      case _kWithErrorSyntax_ErrorAsEnum_Ordinal:
+        final String _name = _kWithErrorSyntax_ErrorAsEnum_Type.name;
+        try {
+          Timeline.startSync(_name);
+          final List<$fidl.MemberType> $types =
+              _kWithErrorSyntax_ErrorAsEnum_Type.response;
+          $decoder.claimMemory(24);
+          $callback(
+            $types[0].decode($decoder, 0),
+          );
+          // ignore: avoid_catches_without_on_clauses
+        } catch (_e) {
+          ctrl.proxyError('Exception handling method response $_name: $_e');
+          ctrl.close();
+          rethrow;
+        } finally {
+          Timeline.finishSync();
+        }
+        break;
+      default:
+        ctrl.proxyError('Unexpected message ordinal: ${$message.ordinal}');
+        ctrl.close();
+        break;
+    }
+  }
+
+  @override
+  void errorAsPrimitive(
+      void callback(WithErrorSyntaxErrorAsPrimitiveResult result)) {
+    if (!ctrl.isBound) {
+      ctrl.proxyError('The proxy is closed.');
+      return;
+    }
+
+    final $fidl.Encoder $encoder = new $fidl.Encoder();
+    $encoder.encodeMessageHeader(_kWithErrorSyntax_ErrorAsPrimitive_Ordinal, 0);
+    Function $zonedCallback;
+    if ((callback == null) || identical(Zone.current, Zone.root)) {
+      $zonedCallback = callback;
+    } else {
+      Zone $z = Zone.current;
+      $zonedCallback = ((WithErrorSyntaxErrorAsPrimitiveResult result) {
+        $z.bindCallback(() {
+          callback(
+            result,
+          );
+        })();
+      });
+    }
+    ctrl.sendMessageWithResponse($encoder.message, $zonedCallback);
+  }
+
+  @override
+  void errorAsEnum(void callback(WithErrorSyntaxErrorAsEnumResult result)) {
+    if (!ctrl.isBound) {
+      ctrl.proxyError('The proxy is closed.');
+      return;
+    }
+
+    final $fidl.Encoder $encoder = new $fidl.Encoder();
+    $encoder.encodeMessageHeader(_kWithErrorSyntax_ErrorAsEnum_Ordinal, 0);
+    Function $zonedCallback;
+    if ((callback == null) || identical(Zone.current, Zone.root)) {
+      $zonedCallback = callback;
+    } else {
+      Zone $z = Zone.current;
+      $zonedCallback = ((WithErrorSyntaxErrorAsEnumResult result) {
+        $z.bindCallback(() {
+          callback(
+            result,
+          );
+        })();
+      });
+    }
+    ctrl.sendMessageWithResponse($encoder.message, $zonedCallback);
+  }
+}
+
+class WithErrorSyntaxBinding extends $fidl.Binding<WithErrorSyntax> {
+  Function _errorAsPrimitiveResponder($fidl.MessageSink $respond, int $txid) {
+    return (WithErrorSyntaxErrorAsPrimitiveResult result) {
+      final $fidl.Encoder $encoder = new $fidl.Encoder();
+      $encoder.encodeMessageHeader(
+          _kWithErrorSyntax_ErrorAsPrimitive_Ordinal, $txid);
+      $encoder.alloc(24 - $fidl.kMessageHeaderSize);
+      final List<$fidl.MemberType> $types =
+          _kWithErrorSyntax_ErrorAsPrimitive_Type.response;
+      $types[0].encode($encoder, result, 0);
+      $respond($encoder.message);
+    };
+  }
+
+  Function _errorAsEnumResponder($fidl.MessageSink $respond, int $txid) {
+    return (WithErrorSyntaxErrorAsEnumResult result) {
+      final $fidl.Encoder $encoder = new $fidl.Encoder();
+      $encoder.encodeMessageHeader(
+          _kWithErrorSyntax_ErrorAsEnum_Ordinal, $txid);
+      $encoder.alloc(24 - $fidl.kMessageHeaderSize);
+      final List<$fidl.MemberType> $types =
+          _kWithErrorSyntax_ErrorAsEnum_Type.response;
+      $types[0].encode($encoder, result, 0);
+      $respond($encoder.message);
+    };
+  }
+
+  @override
+  void handleMessage($fidl.Message $message, $fidl.MessageSink $respond) {
+    final $fidl.Decoder $decoder = new $fidl.Decoder($message);
+    switch ($message.ordinal) {
+      case _kWithErrorSyntax_ErrorAsPrimitive_Ordinal:
+        final String _name = _kWithErrorSyntax_ErrorAsPrimitive_Type.name;
+        try {
+          Timeline.startSync(_name);
+          final List<$fidl.MemberType> $types =
+              _kWithErrorSyntax_ErrorAsPrimitive_Type.request;
+          $decoder.claimMemory(16);
+          impl.errorAsPrimitive(
+            _errorAsPrimitiveResponder($respond, $message.txid),
+          );
+          // ignore: avoid_catches_without_on_clauses
+        } catch (_e) {
+          close();
+          print('Exception handling method call $_name: $_e');
+          rethrow;
+        } finally {
+          Timeline.finishSync();
+        }
+        break;
+      case _kWithErrorSyntax_ErrorAsEnum_Ordinal:
+        final String _name = _kWithErrorSyntax_ErrorAsEnum_Type.name;
+        try {
+          Timeline.startSync(_name);
+          final List<$fidl.MemberType> $types =
+              _kWithErrorSyntax_ErrorAsEnum_Type.request;
+          $decoder.claimMemory(16);
+          impl.errorAsEnum(
+            _errorAsEnumResponder($respond, $message.txid),
+          );
+          // ignore: avoid_catches_without_on_clauses
+        } catch (_e) {
+          close();
+          print('Exception handling method call $_name: $_e');
+          rethrow;
+        } finally {
+          Timeline.finishSync();
+        }
+        break;
+      default:
+        throw new $fidl.FidlError(
+            'Unexpected message name for WithErrorSyntaxBinding');
+    }
+  }
+}
+
 abstract class OvernetInternalProtocol {
   static const String $serviceName = null;
   void methodA(int a, int b);
diff --git a/bin/fidlgen_dart/goldens/protocols.test.fidl.json_test.dart.golden b/bin/fidlgen_dart/goldens/protocols.test.fidl.json_test.dart.golden
index 329337e..38b5f4e 100644
--- a/bin/fidlgen_dart/goldens/protocols.test.fidl.json_test.dart.golden
+++ b/bin/fidlgen_dart/goldens/protocols.test.fidl.json_test.dart.golden
@@ -67,6 +67,18 @@
   }
 }
 
+class WithErrorSyntax$TestBase extends WithErrorSyntax {
+  @override
+  Future<WithErrorSyntaxErrorAsPrimitiveResult> errorAsPrimitive() {
+    return Future.error(UnimplementedError());
+  }
+
+  @override
+  Future<WithErrorSyntaxErrorAsEnumResult> errorAsEnum() {
+    return Future.error(UnimplementedError());
+  }
+}
+
 class OvernetInternalProtocol$TestBase extends OvernetInternalProtocol {
   @override
   Future<void> methodA(int a, int b) {
diff --git a/public/dart/fidl/lib/src/error.dart b/public/dart/fidl/lib/src/error.dart
index 70b0b42..0fbd973 100644
--- a/public/dart/fidl/lib/src/error.dart
+++ b/public/dart/fidl/lib/src/error.dart
@@ -12,3 +12,12 @@
   @override
   String toString() => 'FidlError: $message';
 }
+
+class MethodException<T> implements Exception {
+  MethodException(this.value);
+
+  final T value;
+
+  @override
+  String toString() => 'MethodException: $value';
+}