BoundaryML/baml · error
unexpected result type in InvocationResponse: %T
Error message
unexpected result type in InvocationResponse: %T
What it means
If a successfully-decoded InvocationResponseSuccess contains neither an Object result nor any recognized result variant, the switch on success.GetResult() hits its default branch and reports the concrete Go type of the unexpected result. This guards the protocol: only known result kinds are accepted.
Source
Thrown at engine/language_client_go/pkg/callbacks.go:253
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)
if decodeErr != nil {
safeSend(callback.channel, ResultCallback{Error: fmt.Errorf("failed to decode object handle: %w", decodeErr)})
} else {
safeSend(callback.channel, ResultCallback{HasData: true, Data: decoded})
}
default:
safeSend(callback.channel, ResultCallback{Error: fmt.Errorf("unexpected result type in InvocationResponse: %T", success.GetResult())})
}
}
default:
safeSend(callback.channel, ResultCallback{Error: fmt.Errorf("unexpected response type in InvocationResponse")})
}
safeClose(callback.channel)
callbackMutex.Lock()
defer callbackMutex.Unlock()
deleteCallback(id)
}
func create_unique_id(ctx context.Context, onTick OnTickCallbackData) (uint32, chan ResultCallback) {
id := nextCallbackID.Add(1)
callbackMutex.Lock()
defer callbackMutex.Unlock()
dynamicCallbacks[id] = CallbackData{channel: make(chan ResultCallback, 64), ctx: ctx, onTick: onTick, responseType: responseTypeValue}
callbackLog("[CLIENT_GO_CALLBACK_ADD] id=%d map_size=%d", id, len(dynamicCallbacks))View on GitHub (pinned to bd85ce9dee)
Solutions
- Upgrade the Go bindings to match the native runtime version (the %T in the message identifies the unknown variant).
- Pin both sides to the same BAML release.
- Search the BAML repo for the reported Go type to confirm it's a newer protocol addition.
- File an issue if versions already match — indicates a decoder gap.
Example fix
// before go get github.com/boundaryml/baml@v0.8.0 # older bindings // after go get github.com/boundaryml/baml@latest # match native runtime version go mod tidy
Defensive patterns
Strategy: retry
Try / catch
res := <-ch
if res.Error != nil {
if strings.Contains(res.Error.Error(), "unexpected result type in InvocationResponse") {
// upgrade bindings to match native runtime, then retry once
return retryWithUpgradedBindings(ctx)
}
return res.Error
} Prevention
- Pin BAML Go bindings and native library to the same release
- Watch release notes for new InvocationResponse result variants
- Log the %T value when reporting issues
When it happens
Trigger: The native runtime emits a newer/unknown InvocationResponseSuccess result variant (e.g. after a protocol extension) while the Go bindings are older, so the oneof resolves to an unrecognized case.
Common situations: Version skew between the baml CLI-generated native library and the Go cffi bindings; custom forks adding new result types.
Understand the failure class
Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.
Related errors
- failed to unmarshal content bytes: %w
- failed to marshal object method arguments: %w
- error decoding value, unknown field type:
- error decoding value:
- failed to unmarshal InvocationResponse: %w
AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12).
Data as JSON: /api/errors/d8e3f475c8baa6b2.
Report an issue: GitHub.