Error: Ecto.NoPrimaryKeyValueError at POST /users/register

Hi guys, I’m doing this Pento app from the Programming phoenix liveview book.
When I try to register a new user I’m getting this error from the title with this message:

struct #Pento.Accounts.User<__meta__: #Ecto.Schema.Metadata<:built, "users">, confirmed_at: nil, email: nil, id: nil, inserted_at: nil, updated_at: nil, ...> is missing primary key value

With pointing on the line in this controller:
lib/pento_web/controllers/user_registration_controller.ex

def create(conn, %{“user” => user_params}) do

#this_line
case Accounts.register_user(user_params) do
  {:ok, user} ->
    {:ok, _} =
      Accounts.deliver_user_confirmation_instructions(
        user,
        &Routes.user_confirmation_url(conn, :edit, &1)
        )

With the users schema:

schema “users” do
field :email, :string
field :password, :string, virtual: true, redact: true
field :hashed_password, :string, redact: true
field :confirmed_at, :naive_datetime

timestamps()

Thanks in advance

I would check first if the update action is correct here. Typically they are used for updating existing records, that have an id.

Registering a new user sounds to me like you need a create action.

Thank you and sorry, I edited the question because I made a mistake - I copied the code from the user_settings_controller.ex where is the update function defined. The real problem actually is in the create function in the registration controller

OK, no worries. Can you please post the whole code? Whole controller, whole form and the full request log

1 Like

controller

defmodule PentoWeb.UserRegistrationController do
  use PentoWeb, :controller

  alias Pento.Accounts
  alias Pento.Accounts.User
  alias PentoWeb.UserAuth

  def new(conn, _params) do
    changeset = Accounts.change_user_registration(%User{})
    render(conn, "new.html", changeset: changeset)
  end

  def create(conn, %{"user" => user_params}) do
    case Accounts.register_user(user_params) do
      {:ok, user} ->
        {:ok, _} =
          Accounts.deliver_user_confirmation_instructions(
            user,
            &Routes.user_confirmation_url(conn, :edit, &1)
            )

            conn
            |> put_flash(:info, "User created successfully.")
            |> UserAuth.log_in_user(user)

      {:error, %Ecto.Changeset{} = changeset} ->
        render(conn, "new.html", changeset: changeset)
    end
  end
end

form

<h1>Register</h1>

<.form let={f} for={@changeset} action={Routes.user_registration_path(@conn, :create)}>
  <%= if @changeset.action do %>
    <div class="alert alert-danger">
      <p>Oops, something went wrong! Please check the errors below.</p>
    </div>
  <% end %>

  <%= label f, :email %>
  <%= email_input f, :email, required: true %>
  <%= error_tag f, :email %>

  <%= label f, :password %>
  <%= password_input f, :password, required: true %>
  <%= error_tag f, :password %>

  <div>
    <%= submit "Register" %>
  </div>
</.form>

<p>
  <%= link "Log in", to: Routes.user_session_path(@conn, :new) %> |
  <%= link "Forgot your password?", to: Routes.user_reset_password_path(@conn, :new) %>
</p>

request log

[info] POST /users/register
[debug] Processing with PentoWeb.UserRegistrationController.create/2
  Parameters: %{"_csrf_token" => "aSgrJ2EsAAonQEIqMCI-JxUxcDRWHg8jPCxmRMuiutrYDNQScFBQ0fxO", "user" => %{"email" => "qhbruce@example.com", "password" => "[FILTERED]"}}
  Pipelines: [:browser, :redirect_if_user_is_authenticated]
[debug] QUERY OK source="users" db=5.8ms queue=0.1ms idle=1321.5ms
SELECT TRUE FROM "users" AS u0 WHERE (u0."email" = $1) LIMIT 1 ["qhbruce@example.com"]
↳ Ecto.Changeset.unsafe_validate_unique/4, at: lib/ecto/changeset.ex#1938
[info] Sent 500 in 399ms
[error] #PID<0.731.0> running PentoWeb.Endpoint (connection #PID<0.730.0>, stream id 1) terminated
Server: localhost:4000 (http)
Request: POST /users/register
** (exit) an exception was raised:
    ** (Ecto.NoPrimaryKeyValueError) struct `#Pento.Accounts.User<__meta__: #Ecto.Schema.Metadata<:built, "users">, confirmed_at: nil, email: nil, id: nil, inserted_at: nil, updated_at: nil, ...>` is missing primary key value
        (ecto 3.8.1) lib/ecto/repo/schema.ex:967: anonymous fn/3 in Ecto.Repo.Schema.add_pk_filter!/2
        (elixir 1.13.3) lib/enum.ex:2396: Enum."-reduce/3-lists^foldl/2-0-"/3
        (ecto 3.8.1) lib/ecto/repo/schema.ex:414: Ecto.Repo.Schema.do_update/4
        (pento 0.1.0) lib/pento_web/controllers/user_registration_controller.ex:14: PentoWeb.UserRegistrationController.create/2
        (pento 0.1.0) lib/pento_web/controllers/user_registration_controller.ex:1: PentoWeb.UserRegistrationController.action/2
        (pento 0.1.0) lib/pento_web/controllers/user_registration_controller.ex:1: PentoWeb.UserRegistrationController.phoenix_controller_pipeline/2
        (phoenix 1.6.7) lib/phoenix/router.ex:355: Phoenix.Router.__call__/2
        (pento 0.1.0) lib/pento_web/endpoint.ex:1: PentoWeb.Endpoint.plug_builder_call/2
        (pento 0.1.0) lib/plug/debugger.ex:136: PentoWeb.Endpoint."call (overridable 3)"/2
        (pento 0.1.0) lib/pento_web/endpoint.ex:1: PentoWeb.Endpoint.call/2
        (phoenix 1.6.7) lib/phoenix/endpoint/cowboy2_handler.ex:54: Phoenix.Endpoint.Cowboy2Handler.init/4
        (cowboy 2.9.0) /home/neda/Documents/optimumBA/PhoenixLV/pento/deps/cowboy/src/cowboy_handler.erl:37: :cowboy_handler.execute/2
        (cowboy 2.9.0) /home/neda/Documents/optimumBA/PhoenixLV/pento/deps/cowboy/src/cowboy_stream_h.erl:306: :cowboy_stream_h.execute/3
        (cowboy 2.9.0) /home/neda/Documents/optimumBA/PhoenixLV/pento/deps/cowboy/src/cowboy_stream_h.erl:295: :cowboy_stream_h.request_process/3
        (stdlib 3.17) proc_lib.erl:226: :proc_lib.init_p_do_apply/3

And the code for Accounts.register_user/1 please

  def register_user(attrs) do
    %User{}
    |> User.registration_changeset(attrs)
    |> Repo.update()
  end

Probably that’s the issue. It’s trying to perform an update, which needs the id to already exist. What you are looking for is a Repo.insert(), since it’s a new record.

1 Like

You’re right, I changed it because I had Ecto.ConstraintError and I found that this is the solution to put Repo.update instead. Meanwhile, I reset the base and now I put Repo.insert() and now everything works! Thank you @egze

1 Like