BoundaryML/baml · error

failed to unmarshal InvocationResponse: %w

Error message

failed to unmarshal InvocationResponse: %w

What it means

trigger_callback_object_handle processes the FFI callback that returns an InvocationResponse (e.g. from build_request_from_c). The payload bytes must be valid protobuf for cffi.InvocationResponse; if proto.Unmarshal fails, the error is wrapped and delivered to the waiting channel as the callback result, then the callback is cleaned up.

Source

Thrown at engine/language_client_go/pkg/callbacks.go:228

		}

		safeSend(callback.channel, res)
		if isDone == 1 {
			safeClose(callback.channel)
			callbackMutex.Lock()
			defer callbackMutex.Unlock()
			deleteCallback(id_uint)
		}
	}
}

// trigger_callback_object_handle handles callbacks that return an InvocationResponse
// containing a BamlObjectHandle (e.g. from build_request_from_c).
func trigger_callback_object_handle(id uint32, callback CallbackData, content_bytes []byte) {
	var response cffi.InvocationResponse
	err := proto.Unmarshal(content_bytes, &response)
	if err != nil {
		safeSend(callback.channel, ResultCallback{Error: fmt.Errorf("failed to unmarshal InvocationResponse: %w", err)})
		safeClose(callback.channel)
		callbackMutex.Lock()
		defer callbackMutex.Unlock()
		deleteCallback(id)
		return
	}

	switch resp := response.GetResponse().(type) {
	case *cffi.InvocationResponse_Error:
		safeSend(callback.channel, ResultCallback{Error: BamlError{Message: resp.Error}})
	case *cffi.InvocationResponse_Success:
		success := resp.Success
		if success == nil {
			safeSend(callback.channel, ResultCallback{Error: fmt.Errorf("nil success in InvocationResponse")})
		} else {
			switch result := success.GetResult().(type) {
			case *cffi.InvocationResponseSuccess_Object:
				decoded, decodeErr := decodeRawObjectImpl(callback.runtime, result.Object)

View on GitHub (pinned to bd85ce9dee)

Solutions

  1. Rebuild/reinstall the BAML Go bindings and native runtime together so cffi definitions match (go mod tidy, regenerate, ensure matching baml CLI/runtime versions).
  2. Check that the callback id/payload routing hasn't been corrupted — this is an internal protocol failure, so verify you're on a supported, consistent BAML version.
  3. Inspect the wrapped error from proto.Unmarshal for details (wrong wire type usually indicates a schema mismatch).
  4. Report to BAML if it reproduces on a consistent version set; include the inner unmarshal error.

Example fix

// before
// go.mod pins github.com/boundaryml/baml v0.1.2 but native runtime is v0.9.0

// after
// align versions:
// go get github.com/boundaryml/baml@v0.9.0 && go mod tidy
// ensure the baml CLI that generated the native lib is also v0.9.0
Defensive patterns

Strategy: try-catch

Try / catch

select {
case res := <-ch:
	if res.Error != nil {
		if strings.Contains(res.Error.Error(), "failed to unmarshal InvocationResponse") {
			// version mismatch suspected: reinit runtime/bindings
		}
		return res.Error
	}
case <-timeoutCtx.Done():
	return timeoutCtx.Err()
}

Prevention

When it happens

Trigger: The Rust/FFI side returns callback content_bytes that are not a valid protobuf-encoded InvocationResponse — corrupted or misrouted callback payload, version mismatch between the Go cffi definitions and the native runtime, or a buffer sliced incorrectly.

Common situations: Mixed BAML versions (Go bindings rebuilt against a different native library), custom builds where the callback protocol changed, or memory/transport corruption in the FFI channel.

Understand the failure class

Background: "cannot parse invalid wire-format data", "cannot unmarshal", "failed unmarshalling": protobuf unmarshal errors explained — this error's family across 10 libraries.

Related errors


AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12). Data as JSON: /api/errors/edc0bc5c918301b9. Report an issue: GitHub.