Expected dispatch/2 to return a Plug.Conn

Hello, I am working on a simple Elixir API. Right now, I am trying to develop insert operation. I’m not going to lie, the code below is the output of me trying to fix one problem, which led to another problem. I can see that it returns the result of input, but I also see such error in the terminal:

Server: localhost:4000 (http)
Request: POST /insert
** (exit) an exception was raised:
    ** (RuntimeError) expected dispatch/2 to return a Plug.Conn, all plugs must receive a connection (conn) and return a connection, got: {200, %Plug.Conn{adapter: {Plug.Cowboy.Conn, :...}, assigns: %{}, body_params: %{"tree" => %{"ltree" => nil, "rtree" => %{"ltree" => nil, "rtree" => nil, "value" => 7}}, "value" => 5}, cookies: %Plug.Conn.Unfetched{aspect: :cookies}, halted: false, host: "localhost", method: "POST", owner: #PID<0.374.0>, params: %{"tree" => %{"ltree" => nil, "rtree" => %{"ltree" => nil, "rtree" => nil, "value" => 7}}, "value" => 5}, path_info: ["insert"], path_params: %{}, port: 4000, private: %{before_send: [#Function<1.4266095/1 in Plug.Logger.call/2>], plug_route: {"/insert", #Function<1.105255308/2 in BstInsertion.Endpoint.do_match/4>}}, query_params: %{}, query_string: "", remote_ip: {127, 0, 0, 1}, req_cookies: %Plug.Conn.Unfetched{aspect: :cookies}, req_headers: [{"accept", "*/*"}, {"accept-encoding", "gzip, deflate, br"}, {"connection", "keep-alive"}, {"content-length", "168"}, {"content-type", "application/json"}, {"host", "localhost:4000"}, {"postman-token", "81fcd3ca-36ed-43c6-9b8c-cb32b1e8bd44"}, {"user-agent", "PostmanRuntime/7.28.4"}], request_path: "/insert", resp_body: nil, resp_cookies: %{}, resp_headers: [{"cache-control", "max-age=0, private, must-revalidate"}, {"content-type", "application/json; charset=utf-8"}], scheme: :http, script_name: [], secret_key_base: nil, state: :sent, status: 200}}
        (bst_insertion 0.1.0) lib/bst_insertion/endpoint.ex:1: BstInsertion.Endpoint.plug_builder_call/2
        (bst_insertion 0.1.0) lib/plug/debugger.ex:136: BstInsertion.Endpoint."call (overridable 3)"/2
        (bst_insertion 0.1.0) lib/plug/error_handler.ex:80: BstInsertion.Endpoint.call/2
        (plug_cowboy 2.5.2) lib/plug/cowboy/handler.ex:12: Plug.Cowboy.Handler.init/2
        (cowboy 2.9.0) /Users/x/Desktop/elixir_bst.nosync/BSTInsertion/deps/cowboy/src/cowboy_handler.erl:37: :cowboy_handler.execute/2
        (cowboy 2.9.0) /Users/x/Desktop/elixir_bst.nosync/BSTInsertion/deps/cowboy/src/cowboy_stream_h.erl:306: :cowboy_stream_h.execute/3
        (cowboy 2.9.0) /Users/x/Desktop/elixir_bst.nosync/BSTInsertion/deps/cowboy/src/cowboy_stream_h.erl:295: :cowboy_stream_h.request_process/3
        (stdlib 3.16.1) proc_lib.erl:226: :proc_lib.init_p_do_apply/3

The code in endpoint and router:

  post "/insert" do
    {_status, _body} =
      case conn.body_params do
        %{"tree" => tree, "value" => value} -> {200, process_insert(conn, tree, value)}
        _ -> {400, missing_insert()}
      end
  end
  defp process_insert(conn, tree, value) do
    new_tree = BstInsertion.insert(tree, value)
    conn
    |> put_resp_content_type("application/json")
    |> send_resp(200, Poison.encode!(%{tree: new_tree}))
  end

Whenever I change something in these functions it throws errors at me but in this state, it kind of “works”. However, I am getting above error. I would appreciate any help or hint how to get rid of this error. Thanks!

As the error states, the code passed to post needs to return a Conn struct not a tuple. The minimal change to fix it would be:

  post "/insert" do
    {_status, conn} =
      case conn.body_params do
        %{"tree" => tree, "value" => value} -> {200, process_insert(conn, tree, value)}
        _ -> {400, missing_insert()}
      end

    conn
  end

This could be further simplified by removing the {_status, ...} wrapper, but I’m not sure what that’s intended to do…

2 Likes

You could also skip the case statement completely and pattern match the conn in your process_insert like:

post "/insert" do
  process_insert(conn)
end
defp process_insert(%Plug.Conn{body_params: %{"tree" => tree, "value" => value}} = conn) do
    new_tree = BstInsertion.insert(tree, value)
    conn
    |> put_resp_content_type("application/json")
    |> send_resp(200, Poison.encode!(%{tree: new_tree}))
end

defp process_insert(conn) do 
  # Content of missing_insert/0
end
1 Like

I did not think about that! Thank you, that did the thing!

1 Like