Really weird Ecto error, id query returns '\v' instead of id

Okay, so this is SUPER WEIRD.

Instead of return an id for a query, Ecto is returning a \v for an id!

This is on our production box. Never seen anything like this. Never had even a minor glitch with Ecto.

Any thought on how to troubleshoot this?

As you can see, all of the ids are proper integers.

Am I missing something obvious?

Any help would be most appreciated!

iex(web-phoenix-79-1@192.168.0.12)23> query = from s in Schedule, select: s.id
#Ecto.Query<from s in Polo.Apex.Transfer.Schedule, select: s.id>
iex(web-phoenix-79-1@192.168.0.12)24> Polo.Repo.all(query)                    
15:18:26.520 [debug] Processed SELECT a0."id" FROM "apex_transfer_schedule" AS a0 in 0ms
[2, 5, 3, 6, 11, 7, 4, 8, 1, 9, 10, 12]
iex(web-phoenix-79-1@192.168.0.12)25> query =                                 
...(web-phoenix-79-1@192.168.0.12)25>       from s in Schedule,
...(web-phoenix-79-1@192.168.0.12)25>       where: s.status == "TRANSFER_READY" or s.status == "TRANSFER_ERROR",
...(web-phoenix-79-1@192.168.0.12)25>       select: s.id
#Ecto.Query<from s in Polo.Apex.Transfer.Schedule,
 where: s.status == "TRANSFER_READY" or s.status == "TRANSFER_ERROR",
 select: s.id>
iex(web-phoenix-79-1@192.168.0.12)26> Polo.Repo.all(query)
15:19:14.796 [debug] Processed SELECT a0."id" FROM "apex_transfer_schedule" AS a0 WHERE ((a0."status" = 'TRANSFER_READY') OR (a0."status" = 'TRANSFER_ERROR')) in 0ms
'\v'
iex(web-phoenix-79-1@192.168.0.12)27> Polo.Repo.all(query)|> inspect
15:19:28.379 [debug] Processed SELECT a0."id" FROM "apex_transfer_schedule" AS a0 WHERE ((a0."status" = 'TRANSFER_READY') OR (a0."status" = 'TRANSFER_ERROR')) in 0ms
"'\\v'"
iex(web-phoenix-79-1@192.168.0.12)28> 

Now the same query without the select: s.id:

iex(web-phoenix-79-1@192.168.0.12)30> query =
...(web-phoenix-79-1@192.168.0.12)30>       from s in Schedule,
...(web-phoenix-79-1@192.168.0.12)30>       where: s.status == "TRANSFER_READY" or s.status == "TRANSFER_ERROR"
#Ecto.Query<from s in Polo.Apex.Transfer.Schedule,
 where: s.status == "TRANSFER_READY" or s.status == "TRANSFER_ERROR">
iex(web-phoenix-79-1@192.168.0.12)31> Polo.Repo.all(query)                                                     

15:34:24.140 [debug] Processed SELECT a0."id", a0."apex_account_id", a0."template_id", a0."status", a0."apex_status", a0."target_sell_date", a0."target_transfer_date", a0."response", a0."inserted_at", a0."updated_at" FROM "apex_transfer_schedule" AS a0 WHERE ((a0."status" = 'TRANSFER_READY') OR (a0."status" = 'TRANSFER_ERROR')) in 1ms
[%Polo.Apex.Transfer.Schedule{__meta__: #Ecto.Schema.Metadata<:loaded, "apex_transfer_schedule">,
  apex_account: #Ecto.Association.NotLoaded<association :apex_account is not loaded>,
  apex_account_id: "8CM05208", apex_status: nil,
  apex_transfer_template: #Ecto.Association.NotLoaded<association :apex_transfer_template is not loaded>,
  id: 11, inserted_at: ~N[2018-01-25 01:41:26.289576], response: nil,
  status: "TRANSFER_READY", target_sell_date: nil,
  target_transfer_date: ~D[2018-01-24], template_id: 11,
  updated_at: ~N[2018-01-25 01:41:26.360090]}]

as you can see, the id returned should be 11, not \v.

Just so you can see the code that is calling this:

def get_open_transfers_for_apex() do
    query =
      from s in Schedule,
      where: s.status == "TRANSFER_READY" or s.status == "TRANSFER_ERROR",
      select: s.id

    Polo.Repo.all(query)
  end

About as vanilla as it gets!

and when I run that code:

iex(web-phoenix-79-1@192.168.0.12)37> Polo.Apex.Transfer.Schedule.get_open_transfers_for_apex()                 

15:48:10.003 [debug] Processed SELECT a0."id" FROM "apex_transfer_schedule" AS a0 WHERE ((a0."status" = 'TRANSFER_READY') OR (a0."status" = 'TRANSFER_ERROR')) in 1ms
'\v'
iex(web-phoenix-79-1@192.168.0.12)38> 

[11] (meaning a list with the integer 11) and '\v' are the same in Elixir.

You can try it:

iex(1)> [11] == '\v'
true

iex(2)> [11]
'\v'

So, in a nutshell, you are seeing a \v but your code will work as it is a [11]. You can read more in https://elixir-lang.org/getting-started/binaries-strings-and-char-lists.html#charlists

3 Likes

OMG I am a bozo.

1 Like

Don’t feel bad about it. :slight_smile: We’ve all been there before.
I think this is the most confusing issue in Elixir.

Update: I just learned you can use IO.inspect([11], charlists: :as_lists) too.

1 Like
iex(web-phoenix-79-1@192.168.0.12)42> Enum.each('\v', &IO.puts(&1))
11
:ok
iex(web-phoenix-79-1@192.168.0.12)43> 

:smile:

1 Like

good to know! will remember that…

I had this issue many moons ago when I first started programming in Elixir, but haven’t had it in a while!

Ironically, the code was working fine, it’s just that when I was debugging it by hand to confirm that each function was working, I was shocked to see the query returning \v!

Anyway, thanks for the rapid reply!

I knew it must be something silly, as we have way too many lines of code working for this to have been a real issue.

Okay, one has to admit, this is really weird behavior:

iex(web-phoenix-79-1@192.168.0.12)44> [11]
'\v'
iex(web-phoenix-79-1@192.168.0.12)45> [11,12]
'\v\f'
iex(web-phoenix-79-1@192.168.0.12)46> [1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
iex(web-phoenix-79-1@192.168.0.12)47> [1, 2]         
[1, 2]
iex(web-phoenix-79-1@192.168.0.12)48> [2]   
[2]
iex(web-phoenix-79-1@192.168.0.12)49> [3]
[3]
iex(web-phoenix-79-1@192.168.0.12)50> [10]
'\n'
iex(web-phoenix-79-1@192.168.0.12)51> [0] 
[0]
iex(web-phoenix-79-1@192.168.0.12)52> [1,10]
[1, 10]
iex(web-phoenix-79-1@192.168.0.12)53> [1,11,12]
[1, 11, 12]
iex(web-phoenix-79-1@192.168.0.12)55> [100]    
'd'
iex(web-phoenix-79-1@192.168.0.12)56> [100000]
[100000]
iex(web-phoenix-79-1@192.168.0.12)57> [1000]  
[1000]
iex(web-phoenix-79-1@192.168.0.12)58> [10]  
'\n'
iex(web-phoenix-79-1@192.168.0.12)59> [1] 
[1]

Anyway, THANKS AGAIN!

1 Like

Not necessarily ‘weird’, just a bit of explanation. :slight_smile:

On the BEAM VM a list of integers in the printable range will get ‘pretty printed’ as a string as it is then considered a character list (charlist for short). This is actually the primary string type on the BEAM and using binaries for strings (as elixir does with "blah") is actually far less common on the BEAM overall as character lists can actually be a good bit faster in a lot of cases compared to binaries (not all by far though, and web work tends to be faster with binaries, hence why Elixir uses binaries as its main string type). :slight_smile:

But yep, that is why some were getting printed as character strings and some were printed as lists, it depends on if all elements in the list are integers and all integers in the list are in the printable range. :slight_smile:

You also get the same “issue” in Elixir with binaries and Elixir strings as they are implemented with utf-8 encoded binaries. So for example:

iex(7)> << 97,98,99 >>
"abc"
iex(8)> << 97,98,99 >> == "abc"
true
iex(9)> <<226,132,162>>
"™"
iex(10)> <<226,132,162>> == "™"
true

It’s basically the for same reason: there is no string datatype on the BEAM. So Erlang uses lists of (unicode) characters while Elixir uses binaries containing utf-8 encode characters.

3 Likes