Erlang Error in Process with Exit Value ‘undef’ – Explained

So, I’ve been working on a simple concurrency exercise in Erlang, trying to get communication working across different terminals or shells. The problem is that I keep running into this error every time I try to run init_chat() and enter my name. The error looks like this:

code=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",[]}]}

I have no clue what’s causing this issue, and I’m really stuck. Here’s the full program:

Erlang Code (with Errors):

code-module(chat).
-compile(export_all).

init_chat() ->
In = io:get_line("Name please: "),
Name = string:trim(In),
register(chat_handle, spawn(chat, chat_handle, [Name])).

chat_handle(Name) ->
spawn(chat, message_handle, [Name]),
receive
{message, Sender, Message} ->
io:format("~p: ~p", [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.

message_handle(Name) ->
Message = io:get_line("You: "),
if
Message == "bye/n" ->
disconnect(nodes(), Name);
true ->
send_message(nodes(), Name, Message)
end.

send_message([Head | Tail], Name, Message) ->
{chat_handle, Head} ! {message, Name, Message},
send_message(Tail, Name, Message);
send_message([], Name, Message) ->
message_handle(Name).

disconnect([Head | Tail], Name) ->
{chat_handle, Head} ! {dc, Name},
disconnect(Tail, Name);
disconnect([], Name) ->
{chat_handle, node()} ! quit.

The error message I’m getting, undef, basically means that Erlang can’t find the function chat_handle that I’m trying to call. This usually happens because the function isn’t exported, or there’s a typo, but I’m already using -compile(export_all) at the top of the module. So, I’m not sure why this is happening.

It seems that when the spawn(chat, chat_handle, [Name]) line runs, Erlang can’t find the chat_handle/1 function in the module chat. This is really confusing since the function is right there in the code. Maybe there’s something off with the way I’m handling the process registration or the way spawn is being called.

If you’re working with concurrency in Erlang and attempting to spawn processes that communicate across different nodes or terminals, you might encounter the undef error. This error can be frustrating because it often points to a function or module not being found, even when it seems like it’s defined correctly.

One common issue with concurrency in Erlang is ensuring that the functions you’re trying to call in spawned processes are correctly exported and accessible. In the example above, the function chat_handle/1 is causing an error that looks something like this:

codeError in process <0.91.0> on node kei@osboxes with exit value: {undef,[{chat,chat_handle,"Jeano",[]}]}

This undef error indicates that Erlang cannot find the chat_handle function, even though it’s defined in the same module. The likely cause is that the function is not properly loaded or exported when being called by spawn.

Fix the Error:

  1. Ensure Correct Export of Functions: The first step is to make sure the chat_handle/1 function is exported. While using -compile(export_all) will compile and export all functions, it’s better practice to explicitly export functions. This also helps in catching issues like this early on. We can modify the module to explicitly export the necessary functions.
  2. Correct Function Call: Another possible issue is the way spawn/3 is being called. The function chat_handle/1 should be properly referenced to ensure Erlang can locate it.

Correct Code:

code-module(chat).
-export([init_chat/0, chat_handle/1, message_handle/1, send_message/3, disconnect/2]).

init_chat() ->
In = io:get_line("Name please: "),
Name = string:trim(In),
register(chat_handle, spawn(chat, chat_handle, [Name])).

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.

message_handle(Name) ->
Message = io:get_line("You: "),
if
Message == "bye\n" ->
disconnect(nodes(), Name);
true ->
send_message(nodes(), Name, Message)
end.

send_message([Head | Tail], Name, Message) ->
{chat_handle, Head} ! {message, Name, Message},
send_message(Tail, Name, Message);
send_message([], Name, _Message) ->
message_handle(Name).

disconnect([Head | Tail], Name) ->
{chat_handle, Head} ! {dc, Name},
disconnect(Tail, Name);
disconnect([], _Name) ->
{chat_handle, node()} ! quit.

Key Changes:

  1. Explicit Exports: Instead of using -compile(export_all), I explicitly exported the necessary functions using the -export([...]) directive. This makes sure only the required functions are visible outside the module.
  2. String Comparison Fix: There was an issue with the string comparison in the message_handle/1 function. The string "bye/n" should be "bye\n" to correctly match the newline character returned by io:get_line/1.
  3. Improved Messaging: I adjusted the io:format calls to print messages more cleanly with a newline ~n for better formatting of the output.

By making these adjustments, the undef error should be resolved, allowing the process to properly handle messaging and disconnection across different terminals. This solution ensures that the functions can be found by Erlang when they are spawned or called by other processes.

Related blog posts