Summary
During the :regular collection app-state resync, the Coordinator GenServer crashed with UndefinedFunctionError because ExternalBlobReference.decode/1 was called as a bare module name, but could not be resolved at runtime.
Version: 0.1.0-alpha.7
Elixir: 1.19.5-otp-28
Erlang: 28.1
Rust: 1.94.1
Error
** (UndefinedFunctionError) function ExternalBlobReference.decode/1 is undefined
(module ExternalBlobReference is not available)
ExternalBlobReference.decode(<<10, 32, 119, ...>>)
(baileys_ex) lib/baileys_ex/protocol/proto/noise_messages.ex:143:
BaileysEx.Protocol.Proto.Wire.continue_nested_bytes/5
(baileys_ex) lib/baileys_ex/protocol/proto/message_messages.ex:17:
BaileysEx.Protocol.Proto.MessageSupport.decode_fields/3
(baileys_ex) lib/baileys_ex/syncd/codec.ex:1007:
BaileysEx.Syncd.Codec.decode_collection_patch/2
Root Cause Analysis
Correction: the original issue incorrectly stated the module does not exist. It does exist. Apologies for the initial inaccuracy.
The module is defined at BaileysEx.Protocol.Proto.Syncd.ExternalBlobReference in lib/baileys_ex/protocol/proto/syncd_messages.ex (line 233). The SyncdPatch module (same file, line 155) references it in its @decode_specs module attribute (line 191) as bare ExternalBlobReference:
# syncd_messages.ex — inside defmodule BaileysEx.Protocol.Proto.Syncd do
defmodule SyncdPatch do # line 155
@decode_specs %{
# ...
3 => {:message, 3, ExternalBlobReference}, # line 191 — bare module ref
# ...
}
end
defmodule ExternalBlobReference do # line 233 — defined AFTER SyncdPatch
# ...
end
Since both modules are nested inside BaileysEx.Protocol.Proto.Syncd, the bare ExternalBlobReference reference in @decode_specs should resolve at compile time to the fully-qualified BaileysEx.Protocol.Proto.Syncd.ExternalBlobReference. However, ExternalBlobReference is defined after SyncdPatch in the same file (line 233 vs line 155). This may cause a compile-time ordering issue where the module attribute is evaluated before the sibling module is compiled, resulting in the bare atom ExternalBlobReference being stored unresolved.
At runtime, Wire.continue_nested_bytes/5 calls the module from the decoded spec, and the bare ExternalBlobReference (without namespace) is not a loaded module — hence the UndefinedFunctionError.
Note: codec.ex line 874 references the same module but uses the fully-qualified path Syncd.ExternalBlobReference.decode/1, which works correctly.
Suggested Fix
Either:
- Use the fully-qualified module name in the decode spec (safest):
3 => {:message, 3, BaileysEx.Protocol.Proto.Syncd.ExternalBlobReference}
- Move the
ExternalBlobReference module definition above SyncdPatch in syncd_messages.ex, so it is compiled first and the bare reference resolves correctly.
- Use
__MODULE__-relative aliasing in the decode spec:
alias BaileysEx.Protocol.Proto.Syncd.ExternalBlobReference
# then the bare reference in @decode_specs resolves correctly
Reproduction
- Pair a device and establish a connection with default config (
sync_full_history: true)
- Wait for initial sync — the
:regular collection app-state resync triggers
- Coordinator crashes with the error above
Note: It may depend on the specific app-state patch data returned by the server at sync time — if the patch contains an ExternalBlobReference-typed field, the decode path is triggered.
Summary
During the
:regularcollection app-state resync, the Coordinator GenServer crashed withUndefinedFunctionErrorbecauseExternalBlobReference.decode/1was called as a bare module name, but could not be resolved at runtime.Version: 0.1.0-alpha.7
Elixir: 1.19.5-otp-28
Erlang: 28.1
Rust: 1.94.1
Error
Root Cause Analysis
Since both modules are nested inside
BaileysEx.Protocol.Proto.Syncd, the bareExternalBlobReferencereference in@decode_specsshould resolve at compile time to the fully-qualifiedBaileysEx.Protocol.Proto.Syncd.ExternalBlobReference. However,ExternalBlobReferenceis defined afterSyncdPatchin the same file (line 233 vs line 155). This may cause a compile-time ordering issue where the module attribute is evaluated before the sibling module is compiled, resulting in the bare atomExternalBlobReferencebeing stored unresolved.At runtime,
Wire.continue_nested_bytes/5calls the module from the decoded spec, and the bareExternalBlobReference(without namespace) is not a loaded module — hence theUndefinedFunctionError.Note:
codec.exline 874 references the same module but uses the fully-qualified pathSyncd.ExternalBlobReference.decode/1, which works correctly.Suggested Fix
Either:
ExternalBlobReferencemodule definition aboveSyncdPatchinsyncd_messages.ex, so it is compiled first and the bare reference resolves correctly.__MODULE__-relative aliasing in the decode spec:Reproduction
sync_full_history: true):regularcollection app-state resync triggersNote: It may depend on the specific app-state patch data returned by the server at sync time — if the patch contains an
ExternalBlobReference-typed field, the decode path is triggered.