# protobuf-go-lite Limitations: Understanding the Lack of FieldMask and Extension Support

> Discover protobuf-go-lite limitations, including the absence of FieldMask and proto extensions. Understand why static code generation excludes runtime descriptors and registries.

- Repository: [Aperture Robotics/protobuf-go-lite](https://github.com/aperturerobotics/protobuf-go-lite)
- Tags: deep-dive
- Published: 2026-02-25

---

**protobuf-go-lite deliberately excludes FieldMask and proto extension support because it generates static, reflection-free code that cannot access runtime message descriptors or global extension registries.**

protobuf-go-lite is a stripped-down Protocol Buffers code generator designed for environments where binary size and reflection overhead are unacceptable, such as TinyGo deployments. Unlike the standard `google.golang.org/protobuf` library, it emits static `MarshalVT`, `UnmarshalVT`, size, clone, and equal helpers without relying on the `protoreflect` runtime. This architectural choice creates specific protobuf-go-lite limitations that developers must understand before adoption.

## Why protobuf-go-lite Uses Static Code Generation

The generator ([`generator/base/base.go`](https://github.com/aperturerobotics/protobuf-go-lite/blob/main/generator/base/base.go)) walks `.proto` descriptors at build time and writes Go structs with explicit field tags. It does not emit reflection data structures or register messages with a global type registry. Consequently, any feature requiring runtime introspection—such as examining field descriptors or looking up extensions—is incompatible with this approach.

## No FieldMask Support in protobuf-go-lite

FieldMask support requires runtime introspection to build mask trees and apply partial updates during JSON or protobuf encoding. Since protobuf-go-lite generates reflection-free code, it cannot implement `google.protobuf.FieldMask` functionality.

The [`json/field_mask.go`](https://github.com/aperturerobotics/protobuf-go-lite/blob/main/json/field_mask.go) file defines a minimal `FieldMask` interface, but the generated marshaler and unmarshaler never integrate it. Attempting to use FieldMask helpers results in compile-time errors because the generated messages lack the required reflection methods that standard FieldMask implementations depend on.

## No Proto Extension Support

Proto extensions rely on a global extension registry and runtime reflection to unmarshal unknown fields into extension slots. The protobuf-go-lite generator explicitly excludes extension handling code, and the runtime unmarshaller discards extension data.

In [`generator/base/base.go`](https://github.com/aperturerobotics/protobuf-go-lite/blob/main/generator/base/base.go), the base generator contains the comment `// NOTE: extensions, weak fields not supported.` The unmarshaller in [`features/unmarshal/unmarshal.go`](https://github.com/aperturerobotics/protobuf-go-lite/blob/main/features/unmarshal/unmarshal.go) explicitly states `// NOTE: extensions are not supported.` When unmarshalling payloads containing extensions, the code falls back to storing raw bytes in the `unknownFields` slice:

```go
m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...)

```

This preserves the extension data but makes it inaccessible via standard `proto.GetExtension` or `proto.SetExtension` APIs.

## Additional Unsupported Features

Beyond FieldMasks and extensions, protobuf-go-lite also excludes:

- **Weak fields**: These require runtime descriptor pool lookups, which are commented as unsupported in [`generator/base/base.go`](https://github.com/aperturerobotics/protobuf-go-lite/blob/main/generator/base/base.go)
- **Proto2 JSON serialization**: The JSON feature in [`features/json/json.go`](https://github.com/aperturerobotics/protobuf-go-lite/blob/main/features/json/json.go) explicitly rejects proto2 messages, which frequently rely on extensions

## Working Around protobuf-go-lite Limitations

When using protobuf-go-lite, design your schemas to avoid these constraints:

1. Use explicit optional fields instead of extensions for custom metadata
2. Implement manual field masking by selecting specific fields in application code rather than using `google.protobuf.FieldMask`
3. Store extension-like data as raw bytes in `unknownFields` and parse manually if interoperability with standard protobuf libraries is required

## Summary

- protobuf-go-lite generates static, reflection-free code optimized for binary size in constrained environments
- FieldMask support is impossible without runtime introspection, which the library deliberately excludes to avoid reflection overhead
- Extensions cannot be registered or unmarshalled because the generator omits extension registry code and the runtime discards extension data into `unknownFields`
- These limitations are explicitly documented in [`generator/base/base.go`](https://github.com/aperturerobotics/protobuf-go-lite/blob/main/generator/base/base.go), [`features/unmarshal/unmarshal.go`](https://github.com/aperturerobotics/protobuf-go-lite/blob/main/features/unmarshal/unmarshal.go), and the project README

## Frequently Asked Questions

### Can I add FieldMask support to protobuf-go-lite manually?

No. FieldMask implementation requires reflection APIs to traverse message descriptors dynamically. Since protobuf-go-lite generates code that never imports `protoreflect` or maintains message descriptors at runtime, you cannot implement standard FieldMask behavior without rewriting the generator to include reflection support, which would defeat the library's purpose of minimizing binary size.

### What happens if I try to unmarshal a message containing extensions?

The extension data is stored raw in the `unknownFields` byte slice but remains inaccessible via standard APIs. The unmarshaller in [`features/unmarshal/unmarshal.go`](https://github.com/aperturerobotics/protobuf-go-lite/blob/main/features/unmarshal/unmarshal.go) explicitly skips extension handling and appends unknown field bytes to `unknownFields`. You would need to manually parse these bytes using wire format knowledge to extract extension data, as the standard `proto.GetExtension` functions are not available.

### Is protobuf-go-lite compatible with standard protobuf messages that use extensions?

It can unmarshal the binary data, but it cannot interpret extension fields. If you control both endpoints, avoid extensions when using protobuf-go-lite. If interoperability with standard protobuf libraries is required, treat extension data as opaque unknown fields or redesign the schema to use regular fields instead of extensions, as the lite runtime cannot access extension registries.

### Why does protobuf-go-lite exclude these features while other generators include them?

The library targets TinyGo and other constrained environments where binary size and reflection overhead are unacceptable. The upstream `protobuf-go` library includes a heavy reflection runtime and global registries that increase binary size significantly. protobuf-go-lite trades flexibility for minimal footprint by generating static code that performs marshalling without any runtime descriptor lookups, making it unsuitable for applications requiring dynamic message inspection.