opentofu/opentofu · error
invalid wire format: %w
Error message
invalid wire format: %w
What it means
UnmarshalGraph failed at the very first step: the input bytes could not be parsed as the protobuf wire format of execgraphproto.ExecutionGraph. The bytes are not a valid protobuf message of the expected schema, so no graph elements are even attempted.
Source
Thrown at internal/engine/internal/execgraph/graph_unmarshal.go:36
"github.com/opentofu/opentofu/internal/engine/internal/execgraph/execgraphproto"
"github.com/opentofu/opentofu/internal/lang/eval"
"github.com/opentofu/opentofu/internal/states"
)
// UnmarshalGraph takes some bytes previously returned by [Graph.Marshal] and
// returns a graph that is functionally-equivalent to (but not necessarily
// identical to) the original graph.
//
// Because this is working with data loaded from outside OpenTofu it returns
// errors when encountering problems, but if it fails when unmarshaling an
// unmodified result from [Graph.Marshal] then that represents a bug in either
// this or that function: they should always be updated together so they are
// implementing the same file format.
func UnmarshalGraph(src []byte) (*Graph, error) {
var root execgraphproto.ExecutionGraph
err := proto.Unmarshal(src, &root)
if err != nil {
return nil, fmt.Errorf("invalid wire format: %w", err)
}
elems := root.GetElements()
// During decoding we'll track the typed result ref corresponding to
// each element from the serialized graph, which then allows us to
// make sure that operands are actually of the types they ought to be
// during our validation work.
results := make([]AnyResultRef, len(elems))
builder := NewBuilder()
for idx, elem := range elems {
if !elem.HasRequest() {
// As a special case, a totally-unpopulated result is allowed to
// coerce to any type when we're decoding operation arguments,
// becoming the zero value of the target type.
results[idx] = nil
continue
}View on GitHub (pinned to 3561785c48)
Solutions
- Regenerate the serialized execution graph with a working OpenTofu run instead of repairing the bytes
- Verify the byte slice was produced by Graph.Marshal / the same OpenTofu engine version that is consuming it
- Check for truncation: compare payload length/checksum against what the producer recorded
Defensive patterns
Strategy: try-catch
Validate before calling
// Fail fast on obviously wrong payloads before graph decoding:
// reject empty/truncated buffers and non-proto artifacts.
func plausibleGraphPayload(src []byte) error {
if len(src) == 0 {
return fmt.Errorf("empty serialized graph payload")
}
var probe execgraphproto.ExecutionGraph
if err := proto.Unmarshal(src, &probe); err != nil {
return fmt.Errorf("not a serialized execution graph: %w", err)
}
return nil
} Try / catch
g, err := execgraph.UnmarshalGraph(src)
if err != nil {
if strings.HasPrefix(err.Error(), "invalid wire format") {
return fmt.Errorf("corrupt or foreign payload, regenerate the graph: %w", err)
}
return err
} Prevention
- Transport serialized graphs with checksums (sha256) and verify before unmarshaling
- Write graph artifacts atomically (temp file + rename) to avoid truncation
- Ensure producer and consumer use the same OpenTofu engine build
When it happens
Trigger: Passing truncated, corrupted, or non-graph bytes to UnmarshalGraph; feeding a file that is not a serialized execution graph at all; a payload produced by a proto schema incompatible at the wire level.
Common situations: Reading a truncated or partially-written graph artifact, passing a JSON/text file where binary proto was expected, or transmission corruption between producer and consumer.
Related errors
- invalid operation in element %d: %w
- invalid waiter in element %d: %w
- invalid constant value in element %d: %w
- invalid resource instance address in element %d: %w
- invalid deposed key in element %d: %w
AI-assisted analysis of opentofu/opentofu@3561785c48 (2026-08-15).
Data as JSON: /api/errors/1c123e72327db91d.
Report an issue: GitHub.