diff --git a/CHANGELOG.md b/CHANGELOG.md index 160e3b668..cbb3bdcae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ## 2025-06-24 - Runtime v0.18.1 - fix: subscribing to events should not be blocked by agent actor state [#910](https://github.com/hypermodeinc/modus/pull/910) +- fix: ensure valid UTF8 byte sequence for inference history [#911](https://github.com/hypermodeinc/modus/pull/911) ## 2025-06-23 - Runtime v0.18.0 diff --git a/runtime/db/inferencehistory.go b/runtime/db/inferencehistory.go index 491331f20..a9206205b 100644 --- a/runtime/db/inferencehistory.go +++ b/runtime/db/inferencehistory.go @@ -159,20 +159,23 @@ func getInferenceDataJson(val any) ([]byte, error) { // If the value is a byte slice or string, it must already have been serialized as JSON. // It might be formatted, but we don't care because we store in a JSONB column in Postgres, - // which doesn't preserve formatting. + // which doesn't preserve formatting. For all other types, we serialize to JSON ourselves. + + var bytes []byte switch t := val.(type) { case []byte: - return t, nil + bytes = t case string: - return []byte(t), nil + bytes = []byte(t) + default: + if b, err := utils.JsonSerialize(val); err == nil { + bytes = b + } else { + return nil, err + } } - // For all other types, we serialize to JSON ourselves. - bytes, err := utils.JsonSerialize(val) - if err != nil { - return nil, err - } - return bytes, nil + return utils.SanitizeUTF8(bytes), nil } func WritePluginInfo(ctx context.Context, plugin *plugins.Plugin) {