BoundaryML/baml · error

unexpected response type in InvocationResponse

Error message

unexpected response type in InvocationResponse

What it means

The outer switch on InvocationResponse.GetResponse() expects either an Error or a Success case. Any other (or unset) oneof state falls to the default branch and produces this error, meaning the response message arrived without a valid response variant.

Source

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

	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))
	return id, dynamicCallbacks[id].channel
}

// create_unique_id_for_object creates a callback ID for object-handle responses (e.g. build_request).

View on GitHub (pinned to bd85ce9dee)

Solutions

  1. Upgrade and align BAML Go bindings and native runtime versions.
  2. Check native runtime logs for an internal failure preceding the callback.
  3. Retry the operation once to rule out a transient empty-response race.
  4. Report with versions and a minimal reproduction if persistent.

Example fix

// before
res := <-callbackCh // Error: unexpected response type in InvocationResponse

// after
if res.Error != nil {
    if strings.Contains(res.Error.Error(), "unexpected response type") {
        log.Printf("baml protocol error, check version alignment: %v", res.Error)
    }
    return res.Error
}
Defensive patterns

Strategy: retry

Try / catch

for attempt := 0; attempt < 2; attempt++ {
	res := <-ch
	if res.Error == nil {
		return res.Data, nil
	}
	if !strings.Contains(res.Error.Error(), "unexpected response type in InvocationResponse") || attempt == 1 {
		return nil, res.Error
	}
	// empty/invalid oneof: retry once for transient empty response
}

Prevention

When it happens

Trigger: The native side returns an InvocationResponse with neither Error nor Success set — typically a zero-value/empty protobuf response due to an internal failure before response construction, or a protocol version mismatch that drops the oneof field.

Common situations: Native runtime crash or early-return path that emits an empty response; mismatched Go bindings vs native library; corrupted callback payload that partially decodes.

Related errors


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