I’m working on a simple Erlang chat app that runs across multiple shells. My goal is to let people connect, send messages, and cleanly disconnect. Along the way, I ran into an annoying undef
error and decided to turn this into a learning moment. In this post, I’ll walk you through:
Error in Erlang Chat Code
Here’s the starting point of my chat module:
-module(chat).
-compile(export_all).
%% Start the chat by registering and spawning the handler
init_chat() ->
In = io:get_line("Name please: "),
Name = string:trim(In),
register(chat_handle, spawn(chat, chat_handle, [Name])).
%% Main receive loop for incoming messages and disconnects
chat_handle(Name) ->
spawn(chat, message_handle, [Name]),
receive
{message, Sender, Message} ->
io:format("~p: ~p~n", [Sender, Message]),
chat_handle(Name);
{dc, Sender} ->
io:format("~p has disconnected.~n", [Sender]),
chat_handle(Name);
quit ->
io:format("Disconnecting...~n"),
erlang:halt()
end.
%% Read user input and dispatch accordingly
message_handle(Name) ->
Message = io:get_line("You: "),
if
Message == "bye\n" ->
disconnect(nodes(), Name);
true ->
send_message(nodes(), Name, Message)
end.
%% Broadcast a message to all nodes (including self)
send_message([Head | Tail], Name, Message) ->
{chat_handle, Head} ! {message, Name, Message},
send_message(Tail, Name, Message);
send_message([], Name, _Message) ->
message_handle(Name).
%% Notify peers of disconnect and then signal local shutdown
disconnect([Head | Tail], Name) ->
{chat_handle, Head} ! {dc, Name},
disconnect(Tail, Name);
disconnect([], Name) ->
{chat_handle, node()} ! quit.
This code lets me:
- Ask for a user’s name
- Spawn a process (
chat_handle/1
) that both reads input and handles incoming messages - Broadcast messages to all connected nodes
- Clean up when someone types
bye
Encountering the Undef Error
As soon as I ran init_chat()
on two Erlang shells and typed my name, I saw:
=ERROR REPORT==== 15-Nov-2021::08:13:11.849169 ===
Error in process <0.91.0> on node kei@osboxes
with exit value: {undef,
[{chat, chat_handle, "Jeano", []}]}
That told me something was off with my function calls.
Understanding the Undef Error
undef
means “undefined function.” Erlang can’t find a function with the name and number of arguments you used.- The tuple
{chat, chat_handle, "Jeano", []}
means:- Module:
chat
- Function:
chat_handle
- Arguments: the string
"Jeano"
plus an extra empty list
- Module:
But I only defined chat_handle/1
(one argument). Somewhere, a call tried to invoke chat_handle/2
.
Common causes
- A typo that adds or drops an argument
- Recursively calling the wrong arity, e.g.
chat_handle(Name, [])
- Using
spawn(Module, Function, Args)
with the wrongArgs
list length
Fix the Undef Error
I scanned my code for any chat_handle(
calls and made sure each one had exactly one argument. In my case, the mistake was here:
%% WRONG: accidentally passing two arguments
chat_handle(Name, [])
I corrected it to:
%% RIGHT: only one argument
chat_handle(Name)
Once all calls matched chat_handle/1
, the undef
error went away and the basic chat worked as expected.
Enhancing the Chat Project
To make the project more useful and fun, I added:
- Slash commands (
/nodes
,/private
,/help
,bye
) - Logging of every join, message, and quit to a file
- A cleaner receive loop with a single
chat_loop/1
function
Here’s the enhanced version:
-module(chat).
-compile(export_all).
%% API
init_chat() ->
In = io:get_line("Name please: "),
Name = string:trim(In),
register(chat_handle, spawn(chat, chat_loop, [Name])),
io:format("~nWelcome ~p! Type /help for commands.~n", [Name]).
%% Main loop: spawn reader, then handle incoming events
chat_loop(Name) ->
log(io_lib:format("~p joined.~n", [Name])),
spawn(chat, message_reader, [Name]),
receive
{message, Sender, Text} ->
io:format("~p: ~s", [Sender, Text]),
log(io_lib:format("~p: ~s", [Sender, Text])),
chat_loop(Name);
{dc, Sender} ->
io:format("~p has disconnected.~n", [Sender]),
log(io_lib:format("~p disconnected.~n", [Sender])),
chat_loop(Name);
{nodes_request, From} ->
From ! {nodes_reply, nodes()},
chat_loop(Name);
quit ->
io:format("Disconnecting...~n"),
log(io_lib:format("~p quit.~n", [Name])),
erlang:halt()
end.
%% Read input, handle commands or broadcast
message_reader(Name) ->
io:format("You: "),
line_to_string(io:get_line("")) = Msg,
case string:tokens(string:trim(Msg), " ") of
["/nodes"] ->
chat_handle ! {nodes_request, self()},
receive {nodes_reply, List} ->
io:format("Connected nodes: ~p~n", [List])
end,
message_reader(Name);
["/private", Target|Rest] ->
Text = string:join(Rest, " "),
send_private(Target, Name, Text),
message_reader(Name);
["/help"] ->
io:format("/nodes - list peers~n/private Node Message - send privately~n/bye - exit~n"),
message_reader(Name);
["bye"] ->
disconnect(nodes(), Name);
_ ->
send_broadcast(nodes(), Name, Msg),
message_reader(Name)
end.
send_broadcast([H|T], Name, Msg) ->
{chat_handle, H} ! {message, Name, Msg},
send_broadcast(T, Name, Msg);
send_broadcast([], _, _) -> ok.
send_private(TargetStr, Name, Msg) ->
case list_to_atom(TargetStr) of
Atom ->
Atom ! {message, Name, "[private] " ++ Msg};
_ ->
io:format("Unknown recipient.~n")
end.
disconnect([H|T], Name) ->
{chat_handle, H} ! {dc, Name},
disconnect(T, Name);
disconnect([], _) ->
chat_handle ! quit.
log(Data) ->
File = "/tmp/erlang_chat.log",
{ok, Io} = file:open(File, [append]),
io:format(Io, "~s~n", [Data]),
file:close(Io).
line_to_string({ok, Bin}) -> binary_to_list(Bin);
line_to_string(Other) -> Other.
Practice Exercises
To deepen your Erlang skills, try these:
- Chat Rooms
Let users pick or create a room name. Only broadcast within that room. - OTP Supervision
Convert the chat into agen_server
and supervise it so it restarts on failure. - Message History
Keep the last N messages in memory and replay them when a user joins. - Load Testing
Write a script that spawns dozens of chat clients automatically and measures message latency.
Final Thought
I’m glad I turned a simple undef
error into a learning opportunity. By fixing the arity mismatch and then adding new features, I not only got my chat working smoothly but also explored Erlang’s strengths in concurrency, messaging, and fault tolerance. I hope this walkthrough inspires you to build your own real‑time projects in Erlang and to enjoy the process of finding and fixing errors along the way.