(RuntimeError) expected action/2 to return a Plug.Conn,

hello, I have a simple application that receives a url, assigns a code and must return it. the problem is that the create function in the controller is giving me problems

(RuntimeError) expected action/2 to return a Plug.Conn, all plugs must receive a connection (conn) and return a connection, got: #Ecto.Changeset<action: nil, changes: %{url_code: "95wjw"}, errors: [url: {"can't be blank", [validation: :required]}], data: #Genurl.Models.Url<>, valid?: false>

this is my router

defmodule GenurlWeb.Router do
  use GenurlWeb, :router

  pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_flash
    plug :protect_from_forgery
    plug :put_secure_browser_headers
  end

  pipeline :api do
    plug :accepts, ["json"]
  end

  scope "/", GenurlWeb do
    pipe_through :browser # Use the default browser stack

    get "/", PageController, :index

  end

  # Other scopes may use custom stacks.
   scope "/api/v1", GenurlWeb do
     pipe_through :api

     get "/url/:id", UrlController, :show
     post "/url", UrlController, :create 
   end
end

this is my url.ex:

defmodule Genurl.Models.Url do
    use Ecto.Schema
    
    import Ecto.Changeset
    #import Ecto.Query, only: [from: 2]
  
    alias Genurl.Repo
    alias Genurl.Models.Url
    schema "urls" do
      field :url, :string
      field :url_code, :string
  
      timestamps()
    end
  
    @doc false
    def create_changeset(params) do
      %Url{}
      |> cast(params, [:url])
      |> validate_required([:url])
      |> Ecto.Changeset.change(url_code: get_code_identification())
    end
  
    def get_code_identification() do
      code = gen_reference()
      case Repo.get_by(Url, url: code) do
        :nil ->
          code
        _ ->
          get_code_identification()
      end
    end
  
      def gen_reference() do
        min = String.to_integer("13579", 36)
        max = String.to_integer("AzBxC", 36)
  
        max
        |> Kernel.-(min)
        |> :rand.uniform()
        |> Kernel.+(min)
        |> Integer.to_string(36)
        |> String.downcase
      end
      def create(params) do
        changeset = create_changeset(params)
        case changeset.valid? do
          true ->
            Repo.insert!(changeset)
            {:ok, "Create succesfull"}
          false ->
            {:error, "unable to insert record"}
        end
      end
  
  end

this is my controller:

defmodule GenurlWeb.UrlController do
    use GenurlWeb, :controller
    use Ecto.Schema
    #import Ecto.Changeset
    alias Genurl.Models.Url
    alias Genurl.Repo
    
  
   # action_fallback GenCodeWeb.FallbackController
   
    def index(conn, _params) do
    urls = Repo.all(Genurl.Url)
    render conn, "index.json", urls: urls
    end
  
    #action_fallback GenurlWeb.FallbackController
 
  
    def create(conn, params) do
        IO.inspect ".......................................create"
        with {:ok,  %Url{} = url} <- Genurl.Models.Url.create_changeset(params)do
            IO.inspect ".......................................with"
        conn
        |> put_status(:created)
        |> put_resp_header("location", url_path(conn, :show, url))
        |> render("show.json", %{url: url})
        end
    end

  
    def show(conn, %{"id" => id}) do
        url = Repo.get(Url, id)
        render conn, "show.json", url: url
        end
   
      end

Can you format the code in your post better ? Or paste it in gists for better format.

That issue normally happen when you create a custom Plug, and it should return a conn for the next plug to used. For some reason the return is an %Ecto.Changeset{} object instead of a %Plug.Conn{}. In your code I can’t see any custom Plug in the pipeline definition.

Ecto.Changeset.change returns a Changeset, not a tuple, therefore the clause in the with won’t match and instead the non-matching value is returned.

2 Likes