[error] an exception was raised logging %DBConnection.LogEntry

Set up
-------
phoenix: 1.7.10
elixir 1.16.0-rc.1-otp-26
erlang 26.1.2

I’m working on a basic umbrella app in “Phoenix in Action 1.4”. After finishing chapter 7, where I set up the database, I noticed some output in the terminal window where I started the server (see below). Besides the error in the title of my post, buried in the middle of the output is also this:

(Protocol.UndefinedError) protocol String.Chars not implemented for %Postgrex.Query{…

Edit: I only get those errors when I start the server in the directory:

auction_umbrella/apps/auction_web $ mix phx.server

If I start the server here:

auction_umbrella$ mix phx.server

I don’t get those errors.

Full error output:

1:10:29.869 request_id=F6Sh9FdVahcsEh0AAALh
[error] an exception was raised logging %DBConnection.LogEntry{call: :execute, query: %Postgrex.Query{ref: #Reference<0.4235129421.45875201.249523>, name: “ecto_674”, statement: “SELECT i0."id", i0."title", i0."description", i0."ends_at", i0."inserted_at", i0."updated_at" FROM "items" AS i0”, param_oids: , param_formats: , param_types: , columns: [“id”, “title”, “description”, “ends_at”, “inserted_at”, “updated_at”], result_oids: [20, 1043, 1043, 1114, 1114, 1114], result_formats: [:binary, :binary, :binary, :binary, :binary, :binary], result_types: [Postgrex.Extensions.Int8, Postgrex.Extensions.Raw, Postgrex.Extensions.Raw, Postgrex.Extensions.Timestamp, Postgrex.Extensions.Timestamp, Postgrex.Extensions.Timestamp], types: {Postgrex.DefaultTypes, #Reference<0.4235129421.46006273.115603>}, cache: :reference}, params: , result: {:ok, %Postgrex.Query{ref: #Reference<0.4235129421.45875201.249523>, name: “ecto_674”, statement: “SELECT i0."id", i0."title", i0."description", i0."ends_at", i0."inserted_at", i0."updated_at" FROM "items" AS i0”, param_oids: , param_formats: , param_types: , columns: [“id”, “title”, “description”, “ends_at”, “inserted_at”, “updated_at”], result_oids: [20, 1043, 1043, 1114, 1114, 1114], result_formats: [:binary, :binary, :binary, :binary, :binary, :binary], result_types: [Postgrex.Extensions.Int8, Postgrex.Extensions.Raw, Postgrex.Extensions.Raw, Postgrex.Extensions.Timestamp, Postgrex.Extensions.Timestamp, Postgrex.Extensions.Timestamp], types: {Postgrex.DefaultTypes, #Reference<0.4235129421.46006273.115603>}, cache: :reference}, %Postgrex.Result{command: :select, columns: [“id”, “title”, “description”, “ends_at”, “inserted_at”, “updated_at”], rows: [[2, “My super item”, “A tasty item to please”, ~N[2024-02-22 11:43:39.000000], ~N[2023-12-27 01:39:34.000000], ~N[2023-12-27 01:39:34.000000]], [3, “U2 - Achtung Baby on CD”, “The sound of 4 men chopping down The Joshua Tree”, ~N[2024-02-24 14:30:00.000000], ~N[2023-12-27 01:44:18.000000], ~N[2023-12-27 01:44:18.000000]], [5, “hello”, “world”, ~N[2019-02-22 11:43:39.000000], ~N[2023-12-27 01:57:58.000000], ~N[2023-12-27 01:57:58.000000]], [1, “Old Yeller”, “Computer games and thermonuclear war”, ~N[2019-02-22 11:43:39.000000], ~N[2023-12-27 01:27:51.000000], ~N[2023-12-27 08:08:32.000000]]], num_rows: 4, connection_id: 63442, messages: }}, pool_time: 158833, connection_time: 4078858, decode_time: 19917, idle_time: 1817420099}: ** (Protocol.UndefinedError) protocol String.Chars not implemented for %Postgrex.Query{ref: #Reference<0.4235129421.45875201.249523>, name: “ecto_674”, statement: “SELECT i0."id", i0."title", i0."description", i0."ends_at", i0."inserted_at", i0."updated_at" FROM "items" AS i0”, param_oids: , param_formats: , param_types: , columns: [“id”, “title”, “description”, “ends_at”, “inserted_at”, “updated_at”], result_oids: [20, 1043, 1043, 1114, 1114, 1114], result_formats: [:binary, :binary, :binary, :binary, :binary, :binary], result_types: [Postgrex.Extensions.Int8, Postgrex.Extensions.Raw, Postgrex.Extensions.Raw, Postgrex.Extensions.Timestamp, Postgrex.Extensions.Timestamp, Postgrex.Extensions.Timestamp], types: {Postgrex.DefaultTypes, #Reference<0.4235129421.46006273.115603>}, cache: :reference} of type Postgrex.Query (a struct). This protocol is implemented for the following type(s): Atom, BitString, Date, DateTime, Float, Integer, List, NaiveDateTime, Phoenix.LiveComponent.CID, Time, URI, Version, Version.Requirement
(elixir 1.16.0-rc.1) lib/string/chars.ex:3: String.Chars.impl_for!/1
(elixir 1.16.0-rc.1) lib/string/chars.ex:22: String.Chars.to_string/1
(ecto_sql 3.11.1) lib/ecto/adapters/sql.ex:1207: Ecto.Adapters.SQL.log/4
(db_connection 2.6.0) lib/db_connection.ex:1658: DBConnection.log/5
(ecto_sql 3.11.1) lib/ecto/adapters/postgres/connection.ex:133: Ecto.Adapters.Postgres.Connection.execute/4
(ecto_sql 3.11.1) lib/ecto/adapters/sql.ex:996: Ecto.Adapters.SQL.execute!/5
(ecto_sql 3.11.1) lib/ecto/adapters/sql.ex:952: Ecto.Adapters.SQL.execute/6
(ecto 3.11.1) lib/ecto/repo/queryable.ex:232: Ecto.Repo.Queryable.execute/4

01:10:29.869 request_id=F6Sh9FdVahcsEh0AAALh [info] Sent 200 in 7ms

There are two apps in the umbrella project: auction and auction_web. Here are their mix.exs files:

auction_web:

defmodule AuctionWeb.MixProject do
  use Mix.Project

  def project do
    [
      app: :auction_web,
      version: "0.1.0",
      build_path: "../../_build",
      config_path: "../../config/config.exs",
      deps_path: "../../deps",
      lockfile: "../../mix.lock",
      elixir: "~> 1.14",
      elixirc_paths: elixirc_paths(Mix.env()),
      start_permanent: Mix.env() == :prod,
      aliases: aliases(),
      deps: deps()
    ]
  end

  # Configuration for the OTP application.
  #
  # Type `mix help compile.app` for more information.
  def application do
    [
      mod: {AuctionWeb.Application, []},
      extra_applications: [:logger, :runtime_tools]
    ]
  end

  # Specifies which paths to compile per environment.
  defp elixirc_paths(:test), do: ["lib", "test/support"]
  defp elixirc_paths(_), do: ["lib"]

  # Specifies your project dependencies.
  #
  # Type `mix help deps` for examples and options.
  defp deps do
    [
      {:phoenix, "~> 1.7.10"},
      {:phoenix_html, "~> 3.3"},
      {:phoenix_live_reload, "~> 1.2", only: :dev},
      {:phoenix_live_view, "~> 0.20.1"},
      {:floki, ">= 0.30.0", only: :test},
      {:phoenix_live_dashboard, "~> 0.8.2"},
      {:esbuild, "~> 0.8", runtime: Mix.env() == :dev},
      {:tailwind, "~> 0.2.0", runtime: Mix.env() == :dev},
      {:telemetry_metrics, "~> 0.6"},
      {:telemetry_poller, "~> 1.0"},
      {:gettext, "~> 0.20"},
      {:jason, "~> 1.2"},
      {:plug_cowboy, "~> 2.5"},
      # Stuff I added:
      {:auction, in_umbrella: true},
      {:phoenix_ecto, "~> 4.4.3"}
    ]
  end

  # Aliases are shortcuts or tasks specific to the current project.
  #
  # See the documentation for `Mix` for more info on aliases.
  defp aliases do
    [
      setup: ["deps.get", "assets.setup", "assets.build"],
      "assets.setup": ["tailwind.install --if-missing", "esbuild.install --if-missing"],
      "assets.build": ["tailwind default", "esbuild default"],
      "assets.deploy": ["tailwind default --minify", "esbuild default --minify", "phx.digest"]
    ]
  end
end

auction:

defmodule Auction.MixProject do
  use Mix.Project

  def project do
    [
      app: :auction,
      version: "0.1.0",
      build_path: "../../_build",
      config_path: "../../config/config.exs",
      deps_path: "../../deps",
      lockfile: "../../mix.lock",
      elixir: "~> 1.16-rc",
      start_permanent: Mix.env() == :prod,
      deps: deps()
    ]
  end

  # Run "mix help compile.app" to learn about applications.
  def application do
    [
      extra_applications: [:logger],
      mod: {Auction.Application, []}
    ]
  end

  # Run "mix help deps" to learn about dependencies.
  defp deps do
    [
      # {:dep_from_hexpm, "~> 0.3.0"},
      # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"},
      # {:sibling_app_in_umbrella, in_umbrella: true}
      {:ecto_sql, "~> 3.11.1"},
      {:postgrex, "~> 0.17.4"}
    ]
  end
end

The only config file is auction_umbrella/config/config.exs:

# This file is responsible for configuring your umbrella
# and **all applications** and their dependencies with the
# help of the Config module.
#
# Note that all applications in your umbrella share the
# same configuration and dependencies, which is why they
# all use the same configuration file. If you want different
# configurations or dependencies per app, it is best to
# move said applications out of the umbrella.
import Config

config :auction, ecto_repos: [Auction.Repo]

config :auction, Auction.Repo,
  database: "auction",
  username: "7stud",
  password: "",
  hostname: "localhost",
  port: "5432"

config :auction_web,
  generators: [context_app: false]

# Configures the endpoint
config :auction_web, AuctionWeb.Endpoint,
  url: [host: "localhost"],
  adapter: Phoenix.Endpoint.Cowboy2Adapter,
  render_errors: [
    formats: [html: AuctionWeb.ErrorHTML, json: AuctionWeb.ErrorJSON],
    layout: false
  ],
  pubsub_server: AuctionWeb.PubSub,
  live_view: [signing_salt: "QtLBNvjp"]

# Configure esbuild (the version is required)
config :esbuild,
  version: "0.17.11",
  default: [
    args:
      ~w(js/app.js --bundle --target=es2017 --outdir=../priv/static/assets --external:/fonts/* --external:/images/*),
    cd: Path.expand("../apps/auction_web/assets", __DIR__),
    env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)}
  ]

# Configure tailwind (the version is required)
config :tailwind,
  version: "3.3.2",
  default: [
    args: ~w(
      --config=tailwind.config.js
      --input=css/app.css
      --output=../priv/static/assets/app.css
    ),
    cd: Path.expand("../apps/auction_web/assets", __DIR__)
  ]

# Sample configuration:
#
#     config :logger, :console,
#       level: :info,
#       format: "$date $time [$level] $metadata$message\n",
#       metadata: [:user_id]
#
import Config

# Configures Elixir's Logger
config :logger, :console,
  format: "$time $metadata[$level] $message\n",
  metadata: [:request_id]

# Use Jason for JSON parsing in Phoenix
config :phoenix, :json_library, Jason

# Import environment specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above.
import_config "#{config_env()}.exs"