• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

supabase / supavisor / 22905622957

10 Mar 2026 01:46PM UTC coverage: 79.349% (+3.0%) from 76.338%
22905622957

Pull #841

github

web-flow
Merge 4a025e561 into 85acdde26
Pull Request #841: Improve errors, add error codes

180 of 224 new or added lines in 45 files covered. (80.36%)

10 existing lines in 5 files now uncovered.

2363 of 2978 relevant lines covered (79.35%)

57922.54 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.67
/lib/supavisor/client_handler/error.ex
1
defmodule Supavisor.ClientHandler.Error do
2
  @moduledoc """
3
  Error handling and message formatting for ClientHandler.
4
  """
5

6
  alias Supavisor.{HandlerHelpers, Monitoring.Telem, Protocol.Server}
7

8
  require Supavisor.Protocol.PreparedStatements, as: PreparedStatements
9
  require Logger
10

11
  @type context :: :handshake | :authenticated
12

13
  @doc """
14
  Handles error by logging, sending appropriate error message to the client socket,
15
  recording telemetry failure, and returning gen_statem termination action.
16

17
  Context values:
18
  - `:handshake` - Error during connection setup/auth (records client_join(:fail) telemetry, no ReadyForQuery)
19
  - `:authenticated` - Error after successful auth (no telemetry, sends ReadyForQuery)
20
  """
21
  @spec terminate_with_error(map(), Exception.t(), context()) ::
22
          :gen_statem.handle_event_result()
23
  def terminate_with_error(data, exception, context)
24
      when context in [:handshake, :authenticated] do
25
    error_actions = process(exception, context)
374✔
26
    message = Map.get(error_actions, :error)
374✔
27
    log_message = Map.get(error_actions, :log_message)
374✔
28
    log_level = Map.get(error_actions, :log_level, :error)
374✔
29
    send_ready_for_query = Map.get(error_actions, :send_ready_for_query, false)
374✔
30
    auth_error = Map.get(error_actions, :auth_error, false)
374✔
31

32
    if log_message do
374✔
33
      Logger.log(log_level, "ClientHandler: #{log_message}", auth_error: auth_error)
374✔
34
    end
35

36
    # Only send message if one exists (some errors like socket closed can't send)
37
    if message do
374✔
38
      if send_ready_for_query do
51✔
39
        HandlerHelpers.sock_send(data.sock, [message, Server.ready_for_query()])
12✔
40
      else
41
        HandlerHelpers.sock_send(data.sock, message)
39✔
42
      end
43
    end
44

45
    # Record telemetry failure only during handshake phase
46
    if context == :handshake do
374✔
47
      Telem.client_join(:fail, data.id)
52✔
48
    end
49

50
    {:stop, :normal}
51
  end
52

53
  @spec process(term(), term()) :: map()
54
  defp process(e, stage) when is_exception(e) do
55
    error =
374✔
56
      case e.__struct__.postgres_error(e) do
374✔
57
        nil -> nil
323✔
58
        postgres_error -> Server.encode_error_message(postgres_error)
51✔
59
      end
60

61
    %{
374✔
62
      error: error,
63
      log_message: e.__struct__.log_message(e),
374✔
64
      log_level: e.__struct__.log_level(e),
374✔
65
      # It's very important for the protocol implementation that we send ReadyForQuery after
66
      # fatal errors in authenticated connections. In non authneticated connections, we should
67
      # close without sending ReadyForQuery
68
      send_ready_for_query: stage == :authenticated,
69
      auth_error: e.__struct__.is_auth_error(e)
374✔
70
    }
71
  end
72

73
  defp process(error, context) do
NEW
74
    message = "Internal error (#{context}): #{inspect(error)}"
×
75

76
    %{
×
77
      error: Server.error_message("XX000", message),
78
      log_message: message
79
    }
80
  end
81
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc