title: How to resolve a custom option in a protocol buffer FileDescriptor
body: <p>I'm using protocol buffers 2.5 with Java.  I have a proto file that defines a custom option.  Another proto file uses that custom option.  If I persist the corresponding <code>FileDescriptorProto</code>'s and then read them and convert them to <code>FileDescriptors</code>, the reference to the custom option is manifested as  unknown field.  How do I cause that custom option to be resolved correctly?  </p>
  
  <p>Here's the code. I have two .proto files.  <code>protobuf-options.proto</code> looks like this:</p>
  
  <pre><code>package options;
  
  import "google/protobuf/descriptor.proto";
  
  option java_package = "com.example.proto";
  option java_outer_classname = "Options";
  
  extend google.protobuf.FieldOptions {
      optional bool scrub = 50000;
  }
  </code></pre>
  
  <p>The imported <code>google/protobuf/descriptor.proto</code> is exactly the <code>descriptor.proto</code> that ships with Protocol Buffers 2.5.</p>
  
  <p><code>example.proto</code> looks like this:</p>
  
  <pre><code>package example;
  option java_package = "com.example.protos";
  option java_outer_classname = "ExampleProtos";
  option optimize_for = SPEED;
  option java_generic_services = false;
  
  import "protobuf-options.proto";
  
  message M {
      optional int32 field1 = 1;
      optional string field2 = 2 [(options.scrub) = true];
  }
  </code></pre>
  
  <p>As  you can see, <code>field2</code> references the custom option defined by <code>protobuf-options.proto</code>.</p>
  
  <p>The following code writes a binary-encoded version of all three protos to <code>/tmp</code>:</p>
  
  <pre><code>package com.example;
  
  import com.google.protobuf.ByteString;
  import com.google.protobuf.DescriptorProtos.FileDescriptorProto;
  import com.google.protobuf.Descriptors.FileDescriptor;
  import com.example.protos.ExampleProtos;
  
  import java.io.FileOutputStream;
  import java.io.OutputStream;
  
  /**
   *
   */
  public class PersistFDs {
      public void persist(final FileDescriptor fileDescriptor) throws Exception {
          System.out.println("persisting "+fileDescriptor.getName());
          try (final OutputStream outputStream = new FileOutputStream("/tmp/"+fileDescriptor.getName())) {
              final FileDescriptorProto fileDescriptorProto = fileDescriptor.toProto();
              final ByteString byteString = fileDescriptorProto.toByteString();
              byteString.writeTo(outputStream);
          }
          for (final FileDescriptor dependency : fileDescriptor.getDependencies()) {
              persist(dependency);
          }
      }
      public static void main(String[] args) throws Exception {
          final PersistFDs self = new PersistFDs();
          self.persist(ExampleProtos.getDescriptor());
      }
  }
  </code></pre>
  
  <p>Finally, the following code loads those those protos from <code>/tmp</code>, converts them back into <code>FileDescriptors</code>, and then checks for the custom option on <code>field2</code>:</p>
  
  <pre><code>package com.example;
  
  import com.google.protobuf.ByteString;
  import com.google.protobuf.DescriptorProtos.FileDescriptorProto;
  import com.google.protobuf.Descriptors.FieldDescriptor;
  import com.google.protobuf.Descriptors.FileDescriptor;
  import com.google.protobuf.UnknownFieldSet.Field;
  
  import java.io.FileInputStream;
  import java.io.InputStream;
  
  /**
   *
   */
  public class LoadFDs {
      public FileDescriptorProto loadProto(final String filePath) throws Exception {
          try (final InputStream inputStream = new FileInputStream(filePath)) {
              final ByteString byteString = ByteString.readFrom(inputStream);
              final FileDescriptorProto result = FileDescriptorProto.parseFrom(byteString);
  
              return result;
          }
      }
  
      public static void main(final String[] args) throws Exception {
          final LoadFDs self = new LoadFDs();
  
          final FileDescriptorProto descriptorFDProto = self.loadProto("/tmp/google/protobuf/descriptor.proto");
          final FileDescriptorProto optionsFDProto = self.loadProto("/tmp/protobuf-options.proto");
          final FileDescriptorProto fakeBoxcarFDProto = self.loadProto("/tmp/example.proto");
  
          final FileDescriptor fD = FileDescriptor.buildFrom(descriptorFDProto, new FileDescriptor[0]);
          final FileDescriptor optionsFD = FileDescriptor.buildFrom(optionsFDProto, new FileDescriptor[] { fD });
          final FileDescriptor fakeBoxcarFD = FileDescriptor.buildFrom(fakeBoxcarFDProto, new FileDescriptor[] { optionsFD });
  
          final FieldDescriptor optionsFieldDescriptor = optionsFD.findExtensionByName("scrub");
          if (optionsFieldDescriptor == null) {
              System.out.println("Did not find scrub's FieldDescriptor");
              System.exit(1);
          }
          final FieldDescriptor sFieldDescriptor = fakeBoxcarFD.findMessageTypeByName("M").findFieldByName("field2");
          System.out.println("unknown option fields "+sFieldDescriptor.getOptions().getUnknownFields());
          final boolean hasScrubOption = sFieldDescriptor.getOptions().hasField(optionsFieldDescriptor);
          System.out.println("hasScrubOption: "+hasScrubOption);
  
      }
  }
  </code></pre>
  
  <p>When I run <code>LoadFDs</code>, it fails with this exception: </p>
  
  <blockquote>
    <p>unknown option fields 50000: 1</p>
    
    <p>Exception in thread "main" java.lang.IllegalArgumentException: FieldDescriptor does not match message type.
      at com.google.protobuf.GeneratedMessage$ExtendableMessage.verifyContainingType(GeneratedMessage.java:812)
      at com.google.protobuf.GeneratedMessage$ExtendableMessage.hasField(GeneratedMessage.java:761)
      at com.example.LoadFDs.main(LoadFDs.java:42)</p>
  </blockquote>
  
  <p>The options for <code>FieldDescriptor</code> for the <code>s</code> field ought to have a field for that custom option, but instead it has an unknown field.  The field number and value on the unknown field are correct.  It's just that the custom option is not getting resolved.  How do I fix that?</p>