[Go to site: main page, start]

Skip to content

Lint a Protobuf workspace#

buf lint checks .proto files against a configurable set of style and structural rules. This walkthrough takes a workspace that fails several STANDARD rules, fixes the violations, and shows how to either tighten the rule set or carry forward existing violations on a project that’s adopting linting after the fact.

Prerequisites#

  • Install the Buf CLI.
  • Clone the examples repository and enter the quickstart’s start directory:

    $ git clone git@github.com:bufbuild/buf-examples.git
    $ cd buf-examples/cli/linting/start
    

If you’re new to the Buf CLI, work through the CLI quickstart first.

Workspace layout#

buf.yaml sits at the workspace root and declares a single module under proto/:

cli/linting/start/
.
├── buf.yaml
└── proto
    └── acme
        └── weather
            └── v1
                └── weather.proto

lint.use in buf.yaml controls which lint rules apply. It starts at STANDARD, Buf’s recommended default and the strictest of the built-in categories.

cli/linting/start/buf.yaml
version: v2
modules:
  - path: proto
    name: buf.build/tutorials/lint
lint:
  use:
    - STANDARD
breaking:
  use:
    - FILE

See the buf.yaml reference for every field, and the rules page for what each category covers.

Run buf lint#

Run buf lint from the workspace root:

cli/linting/start/
$ buf lint

proto/acme/weather/v1/weather.proto:17:1:Files with package "weather" must be within a directory "weather" relative to root but were in directory "acme/weather/v1".
proto/acme/weather/v1/weather.proto:17:1:Package name "weather" should be suffixed with a correctly formed version, such as "weather.v1".
proto/acme/weather/v1/weather.proto:20:3:Enum value name "SUNNY" should be prefixed with "CONDITION_".
proto/acme/weather/v1/weather.proto:20:3:Enum zero value name "SUNNY" should be suffixed with "_UNSPECIFIED".
proto/acme/weather/v1/weather.proto:21:3:Enum value name "RAINY" should be prefixed with "CONDITION_".
proto/acme/weather/v1/weather.proto:35:19:RPC request type "Location" should be named "GetWeatherRequest" or "WeatherServiceGetWeatherRequest".

The errors fall into three groups: the package name doesn’t match its directory or carry a version suffix, the enum values don’t follow the CONDITION_ / _UNSPECIFIED naming rules, and the RPC request type doesn’t match its method.

Fix the errors#

Change the package declaration so it matches the directory layout under proto/:

cli/linting/start/proto/acme/weather/v1/weather.proto
 syntax = "proto3";

-package weather;
+package acme.weather.v1;

Rewrite the enum so the zero value is named CONDITION_UNSPECIFIED and every other value carries the CONDITION_ prefix:

cli/linting/start/proto/acme/weather/v1/weather.proto
 enum Condition {
-  SUNNY = 0;
-  RAINY = 1;
+  CONDITION_UNSPECIFIED = 0;
+  CONDITION_SUNNY = 1;
+  CONDITION_RAINY = 2;
 }

For the RPC error, the request message needs to be named after the RPC (GetWeatherRequest). Wrap the existing Location message in a new GetWeatherRequest, leaving Location itself in place for callers to populate:

cli/linting/start/proto/acme/weather/v1/weather.proto
+message GetWeatherRequest {
+  Location location = 1;
+}
+
 message Location {
   float latitude = 1;
   float longitude = 2;
 }

 service WeatherService {
-  rpc GetWeather (Location) returns (GetWeatherResponse);
+  rpc GetWeather (GetWeatherRequest) returns (GetWeatherResponse);
 }

Run buf lint again to confirm the workspace is clean:

cli/linting/start/
$ buf lint

A silent exit means every rule passes.

Customize which rules run#

STANDARD is the recommended baseline, but you can drop individual rules when they don’t fit your style. For example, to keep STANDARD but stop checking that service names end in Service:

cli/linting/start/buf.yaml
version: v2
lint:
  use:
    - STANDARD
  except:
    - SERVICE_SUFFIX

The reverse also works: pick a smaller category like BASIC and add specific rules from STANDARD on top.

cli/linting/start/buf.yaml
version: v2
lint:
  use:
    - BASIC
    - SERVICE_SUFFIX

For the full list of categories and rules, see rules and categories.

Ignore errors on existing files#

Adopting buf lint on an existing project is the harder case: many of the changes the linter wants are breaking changes, so you can’t fix them without coordinating consumers. The standard adoption path is to ignore the violations that exist today, get to a clean lint run, and enforce the rules going forward. Pair that with buf breaking on every schema change so new edits don’t compound the problem.

buf lint --error-format=config-ignore-yaml emits an ignore_only block scoped to the rules still firing:

cli/linting/start/
$ buf lint --error-format=config-ignore-yaml

version: v1
lint:
  ignore_only:
    ENUM_VALUE_PREFIX:
      - acme/weather/v1/weather.proto
    ENUM_ZERO_VALUE_SUFFIX:
      - acme/weather/v1/weather.proto
    RPC_REQUEST_STANDARD_NAME:
      - acme/weather/v1/weather.proto

Two adjustments are needed before pasting this into your buf.yaml:

  1. Drop the version: v1 line; your buf.yaml is already on v2.
  2. Prefix each path with the module’s path (proto/), since the CLI emits paths relative to the module root but buf.yaml interprets them relative to itself.

The result:

cli/linting/start/buf.yaml
version: v2
lint:
  use:
    - STANDARD
  ignore_only:
    ENUM_VALUE_PREFIX:
      - proto/acme/weather/v1/weather.proto
    ENUM_ZERO_VALUE_SUFFIX:
      - proto/acme/weather/v1/weather.proto
    RPC_REQUEST_STANDARD_NAME:
      - proto/acme/weather/v1/weather.proto

Run buf lint once more to confirm a clean state:

cli/linting/start/
$ buf lint

Next steps#