Fiona is a developer with limited kdb+ experience, often finds herself facing a challenge. She needs to execute a function (stored procedure) on a remote kdb+ server maintained by her IT people. Armed with VS Code’s kdb+ plugin (or other tools like KX Developer or kdb+ Studio) she successfully connects to the kdb+ server and inputs the function name, sets the parameters, and hits the run command. However, instead of the expected outcome, she’s greeted by a puzzling 'type error. Just a 'type error, nothing else. Fiona, like many kdb+ users, grapples with debugging and spends significant time tinkering with parameter values until success.

Fiona’s colleague, Laszlo, also encountered a similar roadblock, though his journey to a solution was longer. Although the parameter types seemed correct — some inputs yielded results while others triggered 'type errors. He discovered that the function mishandled empty internal results. For Laszlo, remote debugging became an arduous task.

Local

Debugging is straightforward when you’re running the kdb+ process locally. You’re privy to error locations, variable observations, call stack printing, and the freedom to traverse the stack levels.

q)double: {2*x}
q)add: {x + double y}
q)add[3; `foo]
'type
  [2]  double:{2*x}
                ^
q))x
`foo
q)).Q.bt[]
>>[2]  double:{2*x}
                ^
  [1]  add:{x + double y}
                ^
  [0]  add[3; `foo]
q))`     / move up in the stack
  [1]  add:{x + double y}
                ^
q))x
3

Remote

When dealing with a remote kdb+ process, you meet with a stark 'type error devoid of any contextual information regarding its origin. Assuming the connection handler is referenced as h, and the server holds the definitions of functions double and add, the following occurs:

q)h "add[3; `foo]"
'type
  [0]  h "add[3; `foo]"

Here’s where the extended trap function .Q.trp together with .Q.sbt come to the rescue. You can execute your function in a protected mode and you can capture the call stack in case of errors:

q)1 h ({.Q.trp[value; x; {.Q.sbt y}]}; "add[3; `foo]")
  [4]  double:{2*x}
                ^
  [3]  add:{x + double y}
                ^
  [2]  add[3; `foo]
       ^
  [1]  (.Q.trp)

  [0]  {.Q.trp[value;x;{.Q.sbt y}]}
        ^
1

In scenarios where the value function is restricted on the server due to security concerns, you can devise a projection to obtain a monadic function:

q)1 h ({.Q.trp[add[3]; x; {.Q.sbt y}]}; `foo)

Definition of the function

If you know the name of the function that causes the problem then it might be convenient to look around in the file that defines the function. Probably there are cross references, constants, comments that help you better understand the domain. Function value again comes to rescue. You need to pass the function to value to get some useful information including the file path. We can wrap around this behavior of value by a composition and create function path

q) path: first -3 sublist value@   / output of value is subject to change as per code.kx.com
q) path add
"/path/to/the/file.q"

Bonus tip

Keep an eye out for an upcoming kdb+ release that enhances error handling. Refer to Pierre’s demo at KX Con 2023 for more insights.