** (FunctionClauseError) no function clause matching in StoreApiWeb.FallbackController.call/2

Hey, all ! I’m new to elixir and getting the following error:

** (FunctionClauseError) no function clause matching in StoreApiWeb.FallbackController.call/2

In my routes

  scope "/user", StoreApiWeb do
    pipe_through :browser

    # 获取所有用户信息
    resources "/", UserController, except: [:new, :edit]

    # 用户登录
    post "/sign_in", UserController, :sign_in

In my module

  def get_user_fromname(user_name) do
    #user_name = user["user_name"]
    IO.puts(user_name)
    Repo.all(from u in User, where: u.user_name == ^user_name)
  end

and controllers

 # 用户登录
  def sign_in(conn, %{"user" => %{"user_name" => user_name, "password" => password}}) do
    #conn
    #|> put_status(200)
    #|> json(%{categories: Accounts.get_user_fromname(user_name)})
    with {:ok, %User{} = user} <- Accounts.get_user_fromname(user_name) do
      render(conn, "show.json", user: user)
    end
  end

The error log is

[debug] QUERY OK source="users" db=0.2ms idle=1409.2ms
SELECT u0."id", u0."email", u0."other1", u0."other2", u0."password_hash", u0."role", u0."token", u0."user_name", u0."inserted_at", u0."updated_at" FROM "users" AS u0 WHERE (u0."user_name" = ?) ["gaofeng2"]
鈫?StoreApiWeb.UserController.sign_in/2, at: lib/store_api_web/controllers/user_controller.ex:49
[info] Sent 500 in 20ms
[error] #PID<0.695.0> running StoreApiWeb.Endpoint (connection #PID<0.694.0>, stream id 1) terminated
Server: localhost:4004 (http)
Request: POST /user/sign_in
** (exit) an exception was raised:
    ** (FunctionClauseError) no function clause matching in StoreApiWeb.FallbackController.call/2
        (store_api 0.1.0) lib/store_api_web/controllers/fallback_controller.ex:10: StoreApiWeb.FallbackController.call(%Plug.Conn{adapter: {Plug.Cowboy.Conn, :...}, assigns: %{}, body_params: %{"user" => %{"password" => "123456", "user_name" => "gaofeng2"}}, cookies: %{}, halted: false, host: "localhost", method: "POST", owner: #PID<0.695.0>, params: %{"user" => %{"password" => "123456", "user_name" => "gaofeng2"}}, path_info: ["user", "sign_in"], path_params: %{}, port: 4004, private: %{StoreApiWeb.Router => {[], %{Plug.Swoosh.MailboxPreview => ["mailbox"]}}, :before_send => [#Function<0.23023616/1 in Plug.Telemetry.call/2>], :phoenix_action => :sign_in, :phoenix_controller => StoreApiWeb.UserController, :phoenix_endpoint => StoreApiWeb.Endpoint, :phoenix_format => "json", :phoenix_layout => {StoreApiWeb.LayoutView, :app}, :phoenix_request_logger => {"request_logger", "request_logger"}, :phoenix_router => StoreApiWeb.Router, :phoenix_view => StoreApiWeb.UserView, :plug_session_fetch => #Function<1.77458138/1 in Plug.Session.fetch_session/1>}, query_params: %{}, query_string: "", remote_ip: {127, 0, 0, 1}, req_cookies: %{}, req_headers: [{"accept", "*/*"}, {"accept-encoding", "gzip, deflate, br"}, {"connection", "keep-alive"}, {"content-length", "53"}, {"content-type", "application/json"}, {"host", "localhost:4004"}, {"user-agent", "ApiPOST Runtime +https://www.apipost.cn"}], request_path: "/user/sign_in", resp_body: nil, resp_cookies: %{}, resp_headers: [{"cache-control", "max-age=0, private, must-revalidate"}, {"x-request-id", "FyNjwITHcNBGadkAAAIH"}], scheme: :http, script_name: [], secret_key_base: :..., state: :unset, status: nil}, [%StoreApi.Accounts.User{__meta__: #Ecto.Schema.Metadata<:loaded, "users">, email: "111242", id: 2, inserted_at: ~N[2022-11-01 02:55:23], other1: nil, other2: nil, password: nil, password_hash: "$argon2id$v=19$m=65536,t=8,p=2$/EaNhpO1jHx+Ywr45FLeOA$6XKDG17zfA9nibufXuENYQbqIcugFs8ZY2yvxLSUZS4", role: nil, token: nil, updated_at: ~N[2022-11-01 02:55:23], user_name: "gaofeng2"}])
        (store_api 0.1.0) lib/store_api_web/controllers/user_controller.ex:1: StoreApiWeb.UserController.action/2
        (store_api 0.1.0) lib/store_api_web/controllers/user_controller.ex:1: StoreApiWeb.UserController.phoenix_controller_pipeline/2
        (phoenix 1.6.14) lib/phoenix/router.ex:354: Phoenix.Router.__call__/2
        (store_api 0.1.0) lib/store_api_web/endpoint.ex:1: StoreApiWeb.Endpoint.plug_builder_call/2
        (store_api 0.1.0) lib/plug/debugger.ex:136: StoreApiWeb.Endpoint."call (overridable 3)"/2
        (store_api 0.1.0) lib/store_api_web/endpoint.ex:1: StoreApiWeb.Endpoint.call/2
        (phoenix 1.6.14) lib/phoenix/endpoint/cowboy2_handler.ex:54: Phoenix.Endpoint.Cowboy2Handler.init/4
        (cowboy 2.9.0) d:/project/store_api/deps/cowboy/src/cowboy_handler.erl:37: :cowboy_handler.execute/2
        (cowboy 2.9.0) d:/project/store_api/deps/cowboy/src/cowboy_stream_h.erl:306: :cowboy_stream_h.execute/3
        (cowboy 2.9.0) d:/project/store_api/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

How should I fixed it?

In short, write a function in StoreApiWeb.FallbackController to match the return value of your controller actions.

In detail, learn something about how the action_fallback works - Phoenix.Controller — Phoenix v1.6.14

1 Like

I’ll try this,thank you very much indeed

After reading the document, I still can’t write it. How to write it in fallbackcontroller belongs to get_ user_ The function of fromname, Can you write an example for me?
it is my fallback_controller.ex

defmodule StoreApiWeb.FallbackController do
  @moduledoc """
  Translates controller action results into valid `Plug.Conn` responses.

  See `Phoenix.Controller.action_fallback/1` for more details.
  """
  use StoreApiWeb, :controller

  # This clause handles errors returned by Ecto's insert/update/delete.
  def call(conn, {:error, %Ecto.Changeset{} = changeset}) do
    conn
    |> put_status(:unprocessable_entity)
    |> put_view(StoreApiWeb.ChangesetView)
    |> render("error.json", changeset: changeset)
  end

  # This clause is an example of how to handle resources that cannot be found.
  def call(conn, {:error, :not_found}) do
    conn
    |> put_status(:not_found)
    |> put_view(StoreApiWeb.ErrorView)
    |> render(:"404")
  end

  def call(conn, {:error, %Ecto.Changeset{}}) do
    conn
    |> put_status(:unprocessable_entity)
    |> put_view(StoreApiWeb.ErrorView)
    |> render(:"422")
    end

  def call(conn, {:error, :get_user_fromname}) do
    conn
    |> put_status(:not_found)
    |> put_view(StoreApiWeb.ErrorView)
    |> render(:"404")
  end
end

thank you

Based on these definition, get_user_fromname will return a list of User structs.

This will not match the {:ok, %User{} = user} clause in the with in sign_in, so the fallback controller gets called with the list from get_user_fromname in the second argument.

There are two approaches you can take to fix this:

  • adjust get_user_fromname to return the shape the with is expecting
  • adjust the with to match the shape get_user_fromname is returning
1 Like

Thanks a lot,
I am new in elixir.I don’t understand how to adjust get_user_fromname return the shape.
Can you help me fixed it in the code.

This is returning a list of records, and if you have proper unique constraints in the DB it’s likely just a single record wrapped in a list e.g. [ %User{username: "abc", id: 123} ].

You can change the DB record fetching code to only return a singular record like so:

Repo.get_by(User, user_name: user_name)
1 Like

I’ll try it. Thank you very much

I resolve it
In Module

  def get_user_fromname(user_name) do
       Repo.get_by(User, user_name: user_name)
  end

In controller

  def sign_in(conn, %{"user_name" => user_name, "password" => password}) do
    info = Accounts.get_user_fromname(user_name)
    render(conn, "show.json", user: info)
  end

thanks everyone !

I resolve it
In Module

  def get_user_fromname(user_name) do
       Repo.get_by(User, user_name: user_name)
  end

In controller

  def sign_in(conn, %{"user_name" => user_name, "password" => password}) do
    info = Accounts.get_user_fromname(user_name)
    render(conn, "show.json", user: info)
  end

thank you very much!!!

I am not after farming points or reputation here, but you should know it’s considered a bad form to mark your own comment as a solution when the solution was already given by somebody else.

I don’t mind and I am glad you resolved your problem, it’s simply something to have in mind.

sorry,
I realized my mistake, please accept my sincere apology

No big deal. Just trying to be a good forum mates. :smiley: