Skip to content
Latchkey

protoc "X is already defined" duplicate symbol in CI

protoc builds a single symbol table across all inputs. "is already defined" means the same fully qualified name was declared twice, most often because one .proto is included through two different -I roots and compiled as two distinct files.

What this error means

protoc fails with "common/types.proto: \"example.User\" is already defined in ..." or "was already defined by ...", pointing at two paths that resolve to the same file.

protoc
common/types.proto: "example.common.Address" is already defined in file
"common/types.proto".
common/types.proto: This file is imported twice under different paths.

Common causes

The same proto is reachable via two import roots

Overlapping -I directories let one file be resolved as common/types.proto and types.proto, so protoc compiles and defines it twice.

A file is both listed as input and imported

Passing a proto on the command line while another proto imports it under a different relative path duplicates its symbols.

How to fix it

Give each proto exactly one canonical path

  1. Pick a single import root that every import is relative to.
  2. Remove overlapping -I directories that let a file resolve two ways.
  3. Re-run protoc so each file compiles once.
Terminal
# one root only; all imports are relative to it
protoc -I proto --go_out=gen proto/api/v1/user.proto

Let buf manage the module roots

buf resolves each file to one canonical path from buf.yaml, eliminating double-import duplicate symbols.

Terminal
buf build

How to prevent it

  • Keep a single, non-overlapping import root layout.
  • Do not pass a proto as input if another proto imports it under a different path.
  • Use buf modules so import roots are defined once in buf.yaml.

Related guides

Tired of flaky CI? Latchkey auto-heals failed jobs and retries them for you. Start free →