Problem inserting record with Ecto

Hello, I am tearing my hair out trying to figure out why this doesn’t work. I am probably doing something dumb but I can’t figure it out. I’m very new to Elixir.

I’ve widdled my schema down to next to nothing to try and figure out what’s wrong. Here’s the migration:

defmodule TestApp.Repo.Migrations.CreateUsers do
  use Ecto.Migration

  def change do
    create table(:users, primary_key: false) do
      add :id, :binary_id, primary_key: true
      add :username, :string, null: false
    end 
  end 
end

And the schema file:

defmodule TestApp.Accounts.User do
  use Ecto.Schema
  import Ecto.Changeset

  @primary_key {:id, :binary_id, autogenerate: true}
  schema "users" do
    field :username, :string
  end   

  def changeset(user, attrs) do
    user
    |> cast(attrs, [:username])
  end   
end

And then some code to insert a test object:

TestApp.Accounts.User.changeset(%TestApp.Accounts.User{}, %{username: "ben"})
|> TestApp.Repo.insert()

Running that produces this:

** (Protocol.UndefinedError) protocol String.Chars not implemented for %Postgrex.Query{columns: nil, name: "", param_formats: nil, param_oids: nil, param_types: nil, ref: nil, result_formats: nil, result_oids: nil, result_types: nil, statement: ["INSERT INTO ", [34, "users", 34], [], [32, 40, [[], 34, "id", 34], ") VALUES ", [], 40, [[], 36 | "1"], 41], []], types: nil}. This protocol is implemented for: Atom, BitString, Date, DateTime, Ecto.Date, Ecto.DateTime, Ecto.Time, Float, Integer, List, NaiveDateTime, Time, URI, Version, Version.Requirement
    (elixir) lib/string/chars.ex:3: String.Chars.impl_for!/1
    (elixir) lib/string/chars.ex:22: String.Chars.to_string/1
    (ecto) lib/ecto/adapters/sql.ex:613: Ecto.Adapters.SQL.log/4
    (db_connection) lib/db_connection.ex:1186: DBConnection.log/6
    (db_connection) lib/db_connection.ex:591: DBConnection.prepare_execute/4
    (ecto) lib/ecto/adapters/postgres/connection.ex:86: Ecto.Adapters.Postgres.Connection.execute/4                                                                         
    (ecto) lib/ecto/adapters/sql.ex:256: Ecto.Adapters.SQL.sql_call/6
    (ecto) lib/ecto/adapters/sql.ex:542: Ecto.Adapters.SQL.struct/8
    (ecto) lib/ecto/repo/schema.ex:547: Ecto.Repo.Schema.apply/4
    (ecto) lib/ecto/repo/schema.ex:213: anonymous fn/14 in Ecto.Repo.Schema.do_insert/4                                                                                     
    (elixir) lib/code.ex:677: Code.require_file/2
    (mix) lib/mix/tasks/run.ex:136: Mix.Tasks.Run.run/5
    (mix) lib/mix/tasks/run.ex:76: Mix.Tasks.Run.run/1
    (mix) lib/mix/task.ex:314: Mix.Task.run_task/3
    (mix) lib/mix/cli.ex:80: Mix.CLI.run_task/2

I’ve done mix ecto.drop && mix ecto.create && mix ecto.migrate and all goes fine. I’ve also verified in iex that the changeset is valid. But when passing the changeset to Repo.insert it blows up with that error.

What am I doing wrong? What are some good ways to go about debugging this?

:wave:

Why are you passing :email to changeset when it’s :username that’s casted (and not null in the migration)?

Good catch, thanks, sorry that was a copy paste issue. I fixed the code above, and re-ran it locally and still get the same error.

try autogenerate: false in the schema

Thank you. I changed the schema file to:

defmodule TestApp.Accounts.User do
  use Ecto.Schema
  import Ecto.Changeset

  @primary_key {:id, :binary_id, autogenerate: false}
  schema "users" do
    field :username, :string
  end   

  def changeset(user, attrs) do
    user
    |> cast(attrs, [:username])
  end   
end

And got the same error message :frowning_face:

I looked through the Ecto migration docs and didn’t see anything similar for the migration file. Is there anywhere else autogenerate needs to be declared?

Where do you call

TestApp.Accounts.User.changeset(%TestApp.Accounts.User{}, %{username: "ben"})
|> TestApp.Repo.insert()

from?

I have this code in priv/repo/seeds.exs and run it with mix run priv/repo/seeds.exs :

TestApp.Accounts.User.changeset(%TestApp.Accounts.User{}, %{username: "ben"})
|> TestApp.Repo.insert()

I have also tried the same in an iex prompt started with iex -S mix with identical results. A unit test for the Accounts module also yields the same error.

Can you create a github repo with a way to reproduce the problem? It would help a lot since I don’t see anything wrong with your code snippets at all …

Yes, that is a great idea. I’ll also spin up a DO droplet and try to reproduce it there, just in case there’s something wonky with my local setup. I tried nuking the postgres instance and spinning up a fresh one, but this error occurs before postgrex even sends the query.

The problem seems to be with https://github.com/elixir-ecto/ecto/blob/v2.2.10/lib/ecto/adapters/sql.ex#L613, which means postgrex queries don’t conform to string.chars protocol for some reason which is strange since they should

1 Like

I am still waiting for permission from the client to post the entirety of the code, but I tried to reproduce the issue in a new project and have been unable to :exploding_head:

Was there ever a resolution to this? I just got this exact same error on an application I’ve been working on for months. Cleaned all my deps, recompiled them (same versions), and was able to drop, create, and migrate; but for some reason I can’t insert even the simplest data without that error. This is Elixir 1.7.3,

{:postgrex, ">= 0.0.0"},
{:ecto, "~> 2.1"},

We’d still need a reproducible example. If you can share one, that would be great!

Thanks idiot, that’s fair. We’re not quite ready to open source yet, so that would be non-trivial. Since posting, I did discover that if I clean, get, and compile my deps from my umbrella app’s data application and log into iex from there, everything works normally; but if I back out to my umbrella root, pull in and compile the new deps, and log into iex from there, I get the error. Progress.

It doesn’t appear to have anything to do with the deps. If I clean, get, and compile all deps from the umbrella root, I can write to the db from my umbrella’s repo application, but not from the umbrella root. Strange.

Well, in case anyone else has this trouble and doesn’t know anything more about it than me: killing my _build and rebuilding fixed it, whatever it was.