Skip to content

ArgumentError: 1st argument: not an iodata term #303

Description

@TheArrowsmith

I'm getting an error when trying to send an email with both an HTML and text version using render_body.

I made a minimal app here that replicates the problem. Steps:

  1. Generate a new Phoenix 1.6 app and add phoenix_swoosh as a dependency.
  2. Add a notifier class with a corresponding view:
# lib/email_demo/notifier.ex
defmodule EmailDemo.Notifier do
  import Swoosh.Email
  use Phoenix.Swoosh, view: EmailDemoWeb.NotifierView

  alias EmailDemo.Mailer

  def deliver do
    email =
      new()
      |> to("someone@example.com")
      |> from({"Someone Else", "someoneelse@example.com"})
      |> subject("Subject")
      |> render_body(:email)

    with {:ok, _metadata} <- Mailer.deliver(email) do
      {:ok, email}
    end
  end
end

# lib/email_demo_web/views/notifier_view.ex 
defmodule EmailDemoWeb.NotifierView do
  use EmailDemoWeb, :view
end
  1. Add HTML and text templates:

lib/email_demo_web/templates/notifier/email.text.heex:

This is a plaintext email    

lib/email_demo_web/templates/notifier/email.html.heex:

<p>This is an HTML email</p>                                                                                                                                                                                                                                             
  1. Set things up to deliver an email when you visit the home page:
defmodule EmailDemoWeb.PageController do
  use EmailDemoWeb, :controller

  def index(conn, _params) do
    EmailDemo.Notifier.deliver
    render(conn, "index.html")
  end
end

Now visit the homepage aaaaand... kaboom:

image

Is this a bug, or am I doing something wrong?


To make this searchable I'm going to c&p the whole error and stacktrace:
# ArgumentError at GET /

Exception:

    ** (ArgumentError) errors were found at the given arguments:
    
      * 1st argument: not an iodata term
    
        :erlang.iolist_to_binary(%Phoenix.LiveView.Rendered{static: ["This is a plaintext email\n"], dynamic: #Function&lt;1.35421111/1 in EmailDemoWeb.NotifierView."email.text"/1&gt;, fingerprint: 61109027427193190218149404380223381180, root: false})
        (phoenix_view 2.0.2) lib/phoenix_view.ex:564: Phoenix.View.render_to_string/3
        (phoenix_swoosh 1.2.0) lib/phoenix_swoosh.ex:172: Phoenix.Swoosh.do_render_body/4
        (elixir 1.14.3) lib/enum.ex:2468: Enum."-reduce/3-lists^foldl/2-0-"/3
        (email_demo 0.1.0) lib/email_demo/notifier.ex:13: EmailDemo.Notifier.deliver/0
        (email_demo 0.1.0) lib/email_demo_web/controllers/page_controller.ex:5: EmailDemoWeb.PageController.index/2
        (email_demo 0.1.0) lib/email_demo_web/controllers/page_controller.ex:1: EmailDemoWeb.PageController.action/2
        (email_demo 0.1.0) lib/email_demo_web/controllers/page_controller.ex:1: EmailDemoWeb.PageController.phoenix_controller_pipeline/2
        (phoenix 1.6.16) lib/phoenix/router.ex:354: Phoenix.Router.__call__/2
        (email_demo 0.1.0) lib/email_demo_web/endpoint.ex:1: EmailDemoWeb.Endpoint.plug_builder_call/2
        (email_demo 0.1.0) lib/plug/debugger.ex:136: EmailDemoWeb.Endpoint."call (overridable 3)"/2
        (email_demo 0.1.0) lib/email_demo_web/endpoint.ex:1: EmailDemoWeb.Endpoint.call/2
        (phoenix 1.6.16) lib/phoenix/endpoint/cowboy2_handler.ex:54: Phoenix.Endpoint.Cowboy2Handler.init/4
        (cowboy 2.9.0) /Users/george/arrowsmith/por/email_demo/deps/cowboy/src/cowboy_handler.erl:37: :cowboy_handler.execute/2
        (cowboy 2.9.0) /Users/george/arrowsmith/por/email_demo/deps/cowboy/src/cowboy_stream_h.erl:306: :cowboy_stream_h.execute/3
        (cowboy 2.9.0) /Users/george/arrowsmith/por/email_demo/deps/cowboy/src/cowboy_stream_h.erl:295: :cowboy_stream_h.request_process/3
        (stdlib 4.3) proc_lib.erl:240: :proc_lib.init_p_do_apply/3
    

Code:

`nofile`

    No code available.

  Called with 1 arguments

  * `%Phoenix.LiveView.Rendered{static: ["This is a plaintext email\n"], dynamic: #Function&lt;1.35421111/1 in EmailDemoWeb.NotifierView."email.text"/1&gt;, fingerprint: 61109027427193190218149404380223381180, root: false}`
  
`lib/phoenix_view.ex`

    559   
    560     @doc """
    561     Renders the template and returns a string.
    562     """
    563     def render_to_string(module, template, assign) do
    564&gt;      render_to_iodata(module, template, assign) |&gt; IO.iodata_to_binary()
    565     end
    566   
    567     defp encode(content, template) do
    568       "." &lt;&gt; format = Path.extname(template)
    569   
    
`lib/phoenix_swoosh.ex`

    167   
    168       view =
    169         Map.get(email.private, :phoenix_view) ||
    170           raise "a view module was not specified, set one with put_view/2"
    171   
    172&gt;      content = Phoenix.View.render_to_string(view, template, Map.put(email.assigns, :email, email))
    173       Map.put(email, extension_to_body_key(email, extension), content)
    174     end
    175   
    176     @doc """
    177     Stores the formats for rendering if none was stored yet.
    
`lib/enum.ex`

    No code available.

`lib/email_demo/notifier.ex`

    8       email =
    9         new()
    10         |&gt; to("someone@example.com")
    11         |&gt; from({"Someone Else", "someoneelse@example.com"})
    12         |&gt; subject("Subject")
    13&gt;        |&gt; render_body(:email)
    14   
    15       with {:ok, _metadata} &lt;- Mailer.deliver(email) do
    16         {:ok, email}
    17       end
    18     end
    
`lib/email_demo_web/controllers/page_controller.ex`

    1   defmodule EmailDemoWeb.PageController do
    2     use EmailDemoWeb, :controller
    3   
    4     def index(conn, _params) do
    5&gt;      EmailDemo.Notifier.deliver
    6       render(conn, "index.html")
    7     end
    8   end
    
`lib/email_demo_web/controllers/page_controller.ex`

    1&gt;  defmodule EmailDemoWeb.PageController do
    2     use EmailDemoWeb, :controller
    3   
    4     def index(conn, _params) do
    5       EmailDemo.Notifier.deliver
    6       render(conn, "index.html")
    
`lib/email_demo_web/controllers/page_controller.ex`

    1&gt;  defmodule EmailDemoWeb.PageController do
    2     use EmailDemoWeb, :controller
    3   
    4     def index(conn, _params) do
    5       EmailDemo.Notifier.deliver
    6       render(conn, "index.html")
    
`lib/phoenix/router.ex`

    349           metadata = %{metadata | conn: halted_conn}
    350           :telemetry.execute([:phoenix, :router_dispatch, :stop], measurements, metadata)
    351           halted_conn
    352         %Plug.Conn{} = piped_conn -&gt;
    353           try do
    354&gt;            plug.call(piped_conn, plug.init(opts))
    355           else
    356             conn -&gt;
    357               measurements = %{duration: System.monotonic_time() - start}
    358               metadata = %{metadata | conn: conn}
    359               :telemetry.execute([:phoenix, :router_dispatch, :stop], measurements, metadata)
    
`lib/email_demo_web/endpoint.ex`

    1&gt;  defmodule EmailDemoWeb.Endpoint do
    2     use Phoenix.Endpoint, otp_app: :email_demo
    3   
    4     # The session will be stored in the cookie and signed,
    5     # this means its contents can be read but not tampered with.
    6     # Set :encryption_salt if you would also like to encrypt it.
    
`lib/plug/debugger.ex`

    No code available.

`lib/email_demo_web/endpoint.ex`

    1&gt;  defmodule EmailDemoWeb.Endpoint do
    2     use Phoenix.Endpoint, otp_app: :email_demo
    3   
    4     # The session will be stored in the cookie and signed,
    5     # this means its contents can be read but not tampered with.
    6     # Set :encryption_salt if you would also like to encrypt it.
    
`lib/phoenix/endpoint/cowboy2_handler.ex`

    49             end
    50   
    51           {:plug, conn, handler, opts} -&gt;
    52             %{adapter: {@connection, req}} =
    53               conn
    54&gt;              |&gt; handler.call(opts)
    55               |&gt; maybe_send(handler)
    56   
    57             {:ok, req, {handler, opts}}
    58         end
    59       catch
    
`/Users/george/arrowsmith/por/email_demo/deps/cowboy/src/cowboy_handler.erl`

    32   -optional_callbacks([terminate/3]).
    33   
    34   -spec execute(Req, Env) -&gt; {ok, Req, Env}
    35   	when Req::cowboy_req:req(), Env::cowboy_middleware:env().
    36   execute(Req, Env=#{handler := Handler, handler_opts := HandlerOpts}) -&gt;
    37&gt;  	try Handler:init(Req, HandlerOpts) of
    38   		{ok, Req2, State} -&gt;
    39   			Result = terminate(normal, Req2, State, Handler),
    40   			{ok, Req2, Env#{result =&gt; Result}};
    41   		{Mod, Req2, State} -&gt;
    42   			Mod:upgrade(Req2, Env, Handler, State);
    
`/Users/george/arrowsmith/por/email_demo/deps/cowboy/src/cowboy_stream_h.erl`

    301   	end.
    302   
    303   execute(_, _, []) -&gt;
    304   	ok;
    305   execute(Req, Env, [Middleware|Tail]) -&gt;
    306&gt;  	case Middleware:execute(Req, Env) of
    307   		{ok, Req2, Env2} -&gt;
    308   			execute(Req2, Env2, Tail);
    309   		{suspend, Module, Function, Args} -&gt;
    310   			proc_lib:hibernate(?MODULE, resume, [Env, Tail, Module, Function, Args]);
    311   		{stop, _Req2} -&gt;
    
`/Users/george/arrowsmith/por/email_demo/deps/cowboy/src/cowboy_stream_h.erl`

    290   %% to simplify the debugging of errors. The proc_lib library
    291   %% already adds the stacktrace to other types of exceptions.
    292   -spec request_process(cowboy_req:req(), cowboy_middleware:env(), [module()]) -&gt; ok.
    293   request_process(Req, Env, Middlewares) -&gt;
    294   	try
    295&gt;  		execute(Req, Env, Middlewares)
    296   	catch
    297   		exit:Reason={shutdown, _}:Stacktrace -&gt;
    298   			erlang:raise(exit, Reason, Stacktrace);
    299   		exit:Reason:Stacktrace when Reason =/= normal, Reason =/= shutdown -&gt;
    300   			erlang:raise(exit, {Reason, Stacktrace}, Stacktrace)
    
`proc_lib.erl`

    No code available.


## Connection details

### Params

    %{}

### Request info

  * URI: http://localhost:4000/
  * Query string: 

### Headers
  
  * accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8
  * accept-encoding: gzip, deflate, br
  * accept-language: en-GB,en-US;q=0.9,en;q=0.8
  * connection: keep-alive
  * cookie: _lr_uf_-wly4ri=c1297a67-a292-4fe8-952e-1ca73a54510e; _twittex_key=SFMyNTY.g3QAAAADbQAAAAtfY3NyZl90b2tlbm0AAAAYWGxOV1o4bGptRzkwbkgzNHNFUi1pZHl0bQAAAA5saXZlX3NvY2tldF9pZG0AAAA7dXNlcnNfc2Vzc2lvbnM6Z3Qta1B1U3lhOUxqWjdGV0hITEZjWkpjaGRTcEhnMlRpeElneGdfY1M3RT1tAAAACnVzZXJfdG9rZW5tAAAAIILfpD7ksmvS42exVhxyxXGSXIXUqR4Nk4sSIMYP3Eux.CHLFN4kkQh9nXtGic0M8f9rSWzgc1zqMH63X-ly26Wc; csrftoken=wWhMDQYEifcfCTKhpo7GaE6k9lbvQbA1podKQ6mlls5EiY4AwVr447DJHhRNdPao; _fly_builder_key=SFMyNTY.g3QAAAADbQAAAAtfY3NyZl90b2tlbm0AAAAYa0MyWWpXeDBOQlViLWFwUEQ2VUVpamgwbQAAAA5saXZlX3NvY2tldF9pZG0AAAA7dXNlcnNfc2Vzc2lvbnM6cGdxTFcxLXRDQkJaY282bVFjanR3MFpjNE1GbGpHNm5ual83dVpDMkJGZz1tAAAACnVzZXJfdG9rZW5tAAAAIKYKi1tfrQgQWXKOpkHI7cNGXODBZYxup54_-7mQtgRY.ObIY2uH80F17Wt_LJwOy-cZTGoP1YPG0-x75CLNMzEM; _pensieve_key=SFMyNTY.g3QAAAABbQAAAAtfY3NyZl90b2tlbm0AAAAYZTVIT3gwRlFPN0J6ZElNbHRmVjFQMExF.R2vohSltVMsk459cA21yTfqKUHEiFGmGs6CPbbGwkCk; _blade_key=SFMyNTY.g3QAAAABbQAAAAtfY3NyZl90b2tlbm0AAAAYMFJDMmRnMm5ya1BoNHJPX0JnbXhTS0pZ.GY0zn1OgK5rxMhsxYAGH6JEWNUYy8QgPzwBjVVfQRGM; token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjgxNTY4ODE5LCJpYXQiOjE2ODEzMDk2MTksImp0aSI6ImJlYWNiMjdlMjcwYTQyMjNhMTcyY2RiMmYzODg0YTFjIiwidXNlcl9pZCI6M30.secxRldpkgs9UkgcbPCQne75k7u-KwWtLMZgeUutPd0; refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTY4MTM5NjAxOSwiaWF0IjoxNjgxMzA5NjE5LCJqdGkiOiI2Yzc3MzkzNWZiMmE0YjUyODVkMTFjYzQzYjIzMGEzOCIsInVzZXJfaWQiOjN9.fBO1z9ShpQ9gAqfaFX8rA82eDeRoTjoGfnScLoahgvg; client_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjgxNTY4ODE5LCJpYXQiOjE2ODEzMDk2MTksImp0aSI6ImJlYWNiMjdlMjcwYTQyMjNhMTcyY2RiMmYzODg0YTFjIiwidXNlcl9pZCI6M30.secxRldpkgs9UkgcbPCQne75k7u-KwWtLMZgeUutPd0; client_refresh=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTY4MTM5NjAxOSwiaWF0IjoxNjgxMzA5NjE5LCJqdGkiOiI2Yzc3MzkzNWZiMmE0YjUyODVkMTFjYzQzYjIzMGEzOCIsInVzZXJfaWQiOjN9.fBO1z9ShpQ9gAqfaFX8rA82eDeRoTjoGfnScLoahgvg; partner_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjgxNTY4ODE5LCJpYXQiOjE2ODEzMDk2MTksImp0aSI6ImJlYWNiMjdlMjcwYTQyMjNhMTcyY2RiMmYzODg0YTFjIiwidXNlcl9pZCI6M30.secxRldpkgs9UkgcbPCQne75k7u-KwWtLMZgeUutPd0; partner_refresh=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTY4MTM5NjAxOSwiaWF0IjoxNjgxMzA5NjE5LCJqdGkiOiI2Yzc3MzkzNWZiMmE0YjUyODVkMTFjYzQzYjIzMGEzOCIsInVzZXJfaWQiOjN9.fBO1z9ShpQ9gAqfaFX8rA82eDeRoTjoGfnScLoahgvg; infor_user={%22id%22:3%2C%22url%22:%22http://localhost:8000/api/users/3/%22%2C%22first_name%22:%22Test%22%2C%22last_name%22:%22Client%22%2C%22email%22:%22dev.client@gtintel.io%22%2C%22phone%22:%22%22%2C%22alter_phone%22:null%2C%22authy_id%22:%22%22%2C%22company%22:%22Ground%20Truth%20Intelligence%22%2C%22street_address%22:%2227%20Dev%20Test%20Rd%22%2C%22apartment%22:%22%22%2C%22biography%22:%22%22%2C%22tags%22:[]%2C%22tag_groups%22:[]%2C%22avatar%22:null%2C%22is_client%22:true%2C%22is_administrator%22:false%2C%22is_network_partner%22:false%2C%22accept_batch%22:false%2C%22timezone%22:%22America/New_York%22%2C%22dual_account%22:null%2C%22is_active%22:true%2C%22full_name%22:%22Test%20Client%22%2C%22edit_role%22:false%2C%22is_auto_tag%22:true%2C%22is_deleted%22:false%2C%22notes%22:%22%22%2C%22team%22:{%22id%22:1%2C%22name%22:%22Test%20team%22}%2C%22role%22:%221%22%2C%22id_client%22:3}; type_view=Client%20Mode; tw=eyJhbGciOiJIUzI1NiIsImN0eSI6InR3aWxpby1mcGE7dj0xIiwidHlwIjoiSldUIn0.eyJqdGkiOiJTSzYzYWY2MjcwNWU0Yjk3ZTYyYWY1NTZiODRmNDM1NmQ3LTE2ODEzMTAxNjgiLCJncmFudHMiOnsiZGF0YV9zeW5jIjp7InNlcnZpY2Vfc2lkIjoiSVM2NmE3MzNmYWQ2YzIzNWRjNzM4MzVlMWE1N2ZkNGM2OCJ9LCJjaGF0Ijp7InNlcnZpY2Vfc2lkIjoiSVNjMGQwYTQ0YmQ5YzQ0YmQ1ODY3ZTZmMTRmMzQ4NjM5OCJ9LCJpZGVudGl0eSI6IjMifSwiaXNzIjoiU0s2M2FmNjI3MDVlNGI5N2U2MmFmNTU2Yjg0ZjQzNTZkNyIsImV4cCI6MTY4MTMxMzc2OCwibmJmIjoxNjgxMzEwMTY4LCJzdWIiOiJBQ2M1NzQwZGE5Y2JmYzQyZWZiYTc3ZTYyOTA0NWIxYWJmIn0.2qr8rLSVtfDRzP5QLMbLH9V94Y0lBQ89Yf3f14_XtWo; _trip_key=SFMyNTY.g3QAAAADbQAAAAtfY3NyZl90b2tlbm0AAAAYSmtjUHFMUTRudEJlbEQzNDdHU3BKQ3ZobQAAAA5saXZlX3NvY2tldF9pZG0AAAA7dXNlcnNfc2Vzc2lvbnM6MEMxUHRKdFdqeXZpMmk0b0RsaXlQOEJvS3h6dkRYNmc1N2N1WVZOMTVUQT1tAAAACnVzZXJfdG9rZW5tAAAAINAtT7SbVo8r4touKA5Ysj_AaCsc7w1-oOe3LmFTdeUw.A2Yyb3XvDcjrpAAkLWkG6SfcteZg724v6xeRNnuTMxM; _email_demo_key=SFMyNTY.g3QAAAABbQAAAAtfY3NyZl90b2tlbm0AAAAYbFBrZUZEdmlGTmtWZnlCOVlPdU5UaUFo.4KfqbHoZnjCfirkBczxRssAvay5q1A6B7-Cirm3xMzw
  * host: localhost:4000
  * sec-ch-ua: "Brave";v="111", "Not(A:Brand";v="8", "Chromium";v="111"
  * sec-ch-ua-mobile: ?0
  * sec-ch-ua-platform: "macOS"
  * sec-fetch-dest: document
  * sec-fetch-mode: navigate
  * sec-fetch-site: none
  * sec-fetch-user: ?1
  * sec-gpc: 1
  * upgrade-insecure-requests: 1
  * user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36

### Session

    %{"_csrf_token" =&gt; "lPkeFDviFNkVfyB9YOuNTiAh"}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions