diff --git a/lib/livebook/session/data.ex b/lib/livebook/session/data.ex
index e1e051100..bf0e134ad 100644
--- a/lib/livebook/session/data.ex
+++ b/lib/livebook/session/data.ex
@@ -154,7 +154,7 @@ defmodule Livebook.Session.Data do
# Note that technically the first state is :initial, but we always
# expect app to start evaluating right away, so distinguishing that
# state from :executing would not bring any value
- execution: :executing | :executed | :error,
+ execution: :executing | :executed | :error | :interrupted,
lifecycle: :active | :shutting_down | :deactivated
}
diff --git a/lib/livebook_web/live/app_session_live.ex b/lib/livebook_web/live/app_session_live.ex
index 656d0d504..cbc485905 100644
--- a/lib/livebook_web/live/app_session_live.ex
+++ b/lib/livebook_web/live/app_session_live.ex
@@ -139,22 +139,30 @@ defmodule LivebookWeb.AppSessionLive do
+
+
+
<%= if @data_view.app_status.execution == :error do %>
-
- <.remix_icon icon="error-warning-line" class="text-xl" />
- Something went wrong
-
- <% else %>
-
-
+
+
+ <.remix_icon icon="error-warning-line" class="text-xl" />
+ Something went wrong
+
+ <.link
+ :if={@livebook_authenticated?}
+ navigate={~p"/sessions/#{@session.id}" <> "#cell-#{@data_view.errored_cell_id}"}
+ >
+
Debug
+ <.remix_icon icon="arrow-right-line" />
+
<% end %>
@@ -303,10 +311,19 @@ defmodule LivebookWeb.AppSessionLive do
app_status: data.app_data.status,
show_source: data.notebook.app_settings.show_source,
slug: data.notebook.app_settings.slug,
- multi_session: data.notebook.app_settings.multi_session
+ multi_session: data.notebook.app_settings.multi_session,
+ errored_cell_id: errored_cell_id(data)
}
end
+ defp errored_cell_id(data) do
+ data.notebook
+ |> Notebook.evaluable_cells_with_section()
+ |> Enum.find_value(fn {cell, _section} ->
+ data.cell_infos[cell.id].eval.errored && cell.id
+ end)
+ end
+
defp input_values_for_output(output, data) do
input_ids = for attrs <- Cell.find_inputs_in_output(output), do: attrs.id
Map.take(data.input_values, input_ids)
diff --git a/lib/livebook_web/live/session_live.ex b/lib/livebook_web/live/session_live.ex
index 412c28ba6..2beba296d 100644
--- a/lib/livebook_web/live/session_live.ex
+++ b/lib/livebook_web/live/session_live.ex
@@ -62,6 +62,17 @@ defmodule LivebookWeb.SessionLive do
session = Session.get_by_pid(session_pid)
platform = platform_from_socket(socket)
+ socket =
+ if data.mode == :app do
+ put_flash(
+ socket,
+ :info,
+ "This session is a deployed app. Any changes will be reflected live."
+ )
+ else
+ socket
+ end
+
{:ok,
socket
|> assign(
diff --git a/test/livebook_web/live/app_session_live_test.exs b/test/livebook_web/live/app_session_live_test.exs
index 92f4937d3..003c3e202 100644
--- a/test/livebook_web/live/app_session_live_test.exs
+++ b/test/livebook_web/live/app_session_live_test.exs
@@ -108,7 +108,7 @@ defmodule LivebookWeb.AppSessionLiveTest do
Livebook.App.close(app.pid)
end
- test "only shows an error message when session errors", %{conn: conn} do
+ test "shows an error message when session errors", %{conn: conn} do
slug = Livebook.Utils.random_short_id()
app_settings = %{Livebook.Notebook.AppSettings.new() | slug: slug}
@@ -125,7 +125,8 @@ defmodule LivebookWeb.AppSessionLiveTest do
},
%{
Livebook.Notebook.Cell.new(:code)
- | source: ~s/raise "oops"/
+ | source: ~s/raise "oops"/,
+ id: "error-cell"
}
]
}
@@ -138,12 +139,16 @@ defmodule LivebookWeb.AppSessionLiveTest do
assert_receive {:app_created, %{pid: ^app_pid} = app}
assert_receive {:app_updated,
- %{pid: ^app_pid, sessions: [%{app_status: %{execution: :error}}]}}
+ %{
+ pid: ^app_pid,
+ sessions: [%{id: session_id, app_status: %{execution: :error}}]
+ }}
{:ok, view, _} = conn |> live(~p"/apps/#{slug}") |> follow_redirect(conn)
+ assert render(view) =~ "Printed output"
assert render(view) =~ "Something went wrong"
- refute render(view) =~ "Printed output"
+ assert render(view) =~ ~p"/sessions/#{session_id}" <> "#cell-error-cell"
Livebook.App.close(app.pid)
end