Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ and this project adheres to

### Fixed

- Return a clean error instead of crashing when the AI assistant's streaming
endpoint responds with a non-success status.
[#4941](https://github.com/OpenFn/lightning/issues/4941)

## [2.16.8] - 2026-07-01

## [2.16.8-pre] - 2026-06-18
Expand Down
10 changes: 8 additions & 2 deletions lib/lightning/ai_assistant/ai_assistant.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1434,10 +1434,10 @@ defmodule Lightning.AiAssistant do
case error_response do
{:ok, %Tesla.Env{status: status, body: body}}
when status not in @success_status_range ->
error_message = body["message"]
error_message = error_message_from_body(body)

Logger.error(
"AI query failed for session #{session.id}: #{error_message}"
"AI query failed for session #{session.id} (status #{status}): #{error_message}"
)

{:error, error_message}
Expand All @@ -1459,6 +1459,12 @@ defmodule Lightning.AiAssistant do
end
end

defp error_message_from_body(%{"message" => message}) when is_binary(message),
do: message

defp error_message_from_body(_body),
do: "Oops! Something went wrong. Please try again."

defp build_job_message(body) do
message = body["history"] |> Enum.reverse() |> hd()
message_attrs = Map.take(message, ["role", "content"])
Expand Down
33 changes: 33 additions & 0 deletions test/lightning/ai_assistant/ai_assistant_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -3392,6 +3392,39 @@ defmodule Lightning.AiAssistantTest do
assert_received {:ai_assistant, :streaming_error,
%{error: "Streaming connection lost", session_id: _}}
end

test "returns a clean binary error tuple for a non-2xx response with any body shape",
%{user: user, workflow: %{jobs: [job_1 | _]}} do
session =
insert(:chat_session,
user: user,
job: job_1,
messages: [
%{
role: :user,
content: "help me",
user: user,
status: :pending,
inserted_at: DateTime.utc_now() |> DateTime.add(-1)
}
]
)

# Each shape (Stream function, %Stream{} struct, nested-message map)
# previously crashed the handler; assert it now yields a binary error.
for body <- [
Stream.unfold(:done, fn _ -> nil end),
Stream.map([], & &1),
%{"message" => %{"detail" => "invalid input"}}
] do
expect(Lightning.Tesla.Mock, :call, fn _env, _opts ->
{:ok, %Tesla.Env{status: 500, body: body}}
end)

assert {:error, message} = AiAssistant.query_stream(session, "help me")
assert is_binary(message)
end
end
end

describe "query_workflow_stream/3" do
Expand Down