Making sense of Go errors
I've been programming Go on and off for the past few months and errors still confuse me. This might sound surprising since Go errors are pretty simple, but their simplicity also betrays the fact that there's no standardized way of structuring them in larger programs, which makes me endlessly confused when navigating different codebases.
There's a really good blog post from 2016 elaborating on errors. It's old though and some features have been added since then. I also figured it'd be better for me to internalize them if I wrote about them myself.
First, Go has no exceptions. Instead, functions that can error just return the error. Go doesn't have union types so instead functions just return potentially multiple values, with the error as one of the values. Error handling and Go gives this example:
func Open(name string) (file *File, err error)
error
is an interface and if it's not nil
, that means an error has happened and you need to handle that.
So, let's say you want to open a file, so you call Open
. You check that the second value it returns is not nil
. What to do then?
First, we need to consider the context. Why are you opening a file? Let's say you have a script that takes a filename from the user and tries to open it. You might get an error because there's no such file with the name provided, in which case the error will be ErrNotExist
. You can check for that error (using errors.Is(err, ErrNotExist)
) and tell the user that the file doesn't exist, for example. In another context, you might have a long-running program (a database, for example) and you can't keep everything in memory so you need to read files at various times. Most of the time you'll expect the files to exist. If they don't then something very wrong has happened and you need to be ready to recover from that somehow.
Now, none of this is particular to Go. Errors are a fact of life and we always need to handle them. In languages with exceptions they can go under the radar since you're not forced to handle them: you can just let them bubble up to the caller, which can lead to an exception blowing up very far from the code that caused it. In Go you're not forced to handle them either, unlike in say Rust or Zig, where you have to explicitly ignore them. Still, the fact that errors get returned from functions makes Go's error handling more explicit than languages with exceptions.
So, what do Go errors look like? First, error
is just an interface:
type error interface {
Error() string
}
It's common to define sentinel errors which are effectively just constants, created with errors.New()
. One example is the aforementioned ErrNotExist
. It can be checked with a direct equality comparison err == ErrNotExist
. However it's preferable to check with errors.Is(err, ErrNotExist)
. This is because of error wrapping. You can wrap an error into another error type by implementing the method Unwrap() error
in the wrapping error type.
For example, I could create a type that wraps filesystem errors with some contextual information on where in the program the operation was taking place. With that I lose the ability to check for ErrNotExist
through a direct equality comparison, but I can still check for it with errors.Is()
.
There's also errors.As()
which can match an error against a particular error type, as opposed to a particular error value. Also, you can wrap errors on the fly without creating new error types, passing the error fmt.Errorf()
and using the %w
specifier, like fmt.Errorf("failed to open file: %w", err)
. This is useful for adding a "stack trace" of sorts to errors while still allowing for checking for them with errors.Is()
.
Dave Cheney's blog post recommends avoiding sentinel errors, because they become part of your module's public interface and create coupling. He instead advocates for having "opaque" error values that you're not meant to inspect; if you do need inspecting you can instead define an interface and check if the error implements the interface, through interface conversion:
type fileError interface {
IsDatabaseFile() bool
}
func open(name string) (*File, error) {
file, err := Open(name)
if err != nil {
t, ok := err.(fileError)
if ok && t.IsDatabaseFile() {
// handle database file error
}
// ...
}
// ...
}
Since Go's interface are implicit this doesn't create coupling between modules. It's a quirky and unusual-looking solution to me, compared to sentinel errors. But I can see myself being swayed if I ever worked in a large Go codebase.
The blog post was written before error wrapping was added to Go but it does suggest a solution that looks a lot like what eventually made it into the language.
Walking up stairs in Godot
Godot's system for collision detection works pretty well out of the box for floors, walls and ramps, but some additional work is required if you want to keep your characters from getting stuck on small objects on the floor while walking. More importantly you might also want them to be able to walk up stairs without jumping.
There are different ways of getting around it. One is to pretend that your stairs are really ramps. A better solution is to detect that you're trying to walk into a stair step and teleport you up as you move forward. There's actually a node for this (SeparationRayShape3D) in Godot, but instead I want to talk about a more manual and more tweakable solution that I first found in this video but later in other places as well. The video is pretty good but it focuses more on the implementation rather than explanations and I needed to do some thinking and playing around to understand how it works and what the different values do.
The idea is to continuously run a body motion test to check if stepping forward would lead you to collide with a stair step (or a short object like a small rock). We want to find out how far we'd make if there was no stairs, and then teleport up from that point so that we're right above the stairs.


Basically we should move from the situation on the left to the situation on the right above.
Godot has a PhysicsServer3D.body_test_motion()
function that we can use to conduct the motion test. It performs the motion and then returns (in the result
argument) the point of collision. We can tweak the basic idea above so that the collision point returned is precisely the point we want to teleport to. The strategy is to first move up and then forward (by the amount we would normally move horizontally). This will be the initial position (the from
parameter) in the test. Then we move down, by setting the motion
parameter to a value that will eventually reach the stair step. If we collide then we know that we are running into a stair step, and we'll also know exactly where we should teleport to.
The following poorly made diagram shows the rough idea. (I really should get better at making these sometime.)

The blue arrow will determine the height we're starting the test from, and it should be higher than the step height. The purple arrow is the character's expected movement, assuming there was no stairs here. This will be equal to the horizontal velocity. Blue + purple gives the initial position of the test (i.e. the from
parameter). We then move down by the green arrow (the motion
parameter), whose length can be equal to the blue arrow.
There are some additional details to pay attention to when implementing this. One is that you have to be careful not to allow moving up stairs that are too tall, otherwise you'll also be able to go up walls and such. So you need to define some maximum height your stair steps are allowed to be. Also, as noted earlier, when moving up it's important to move up above that maximum height, and when performing the test you need to move sufficiently down to make sure you reach the step (otherwise you might not collide even though there's a step below). The video I linked uses 2 * max_step_height
for both going up and down, which seems to work well in practice. In the picture above the arrows' lengths are clearly less than 2 * max_step_height
, which probably also works OK but there might have some cases I haven't thought of.
The final step is to check that the surface we're teleporting to is not too steep. We could do that by checking the collision normal, but there there's an additional complication. Because the body test motion uses the character's collision shape to detect collision, it can happen that the collision point ends up at the edge of the step instead of on top of it. One way to work around that is to use a RayCast3D node. We can place it at the collision point, then move it up a little bit (by a distance a bit below the raycast's length) and then move it forward a little bit (towards the step). We can then check the collision normal for that and if it's not too steep then we're good. This is just a matter of checking that collision_normal.angle_to(Vector3.UP) <= floor_max_angle
.
When working through this I really appreciated being able to change values and see them immediately in the game. This will perhaps become less relevant over time but it's immensely valuable for learning and prototyping.
Bamboozled by elisp
I've been using Emacs for nearly 20 years now, but I still suck at writing elisp. I've never made an effort to learn it seriously and that's something I'd like to start correcting in the near future. I have some ideas. I have an ever-growing collection of scripts I use at work to make several minor tasks less painful, mostly having to do with checking on various AWS resources and updating entries in AirTable. Can I learn enough elisp to move these tools into Emacs, maybe leveraging tabulated-list-mode
and/or transient
? Who knows. The scripts will probably stay for a while but this could be a fun exercise.
I have a hard time navigating the documentation. I don't have much to complain about - the only specific complaint I have is that it sorely lacks examples - but it just doesn't feel like a good place to hang around. I don't know, it just feels old? The terminology is strange and foreign to me (even after 20 years of Emacs), and basic functions feel underpowered and limited. For example just today I learned about pcase
. It is actually a very powerful construct; you can even use it to destructure forms. But I really wish you could destructure objects instead (so I could deconstruct an alist, for example). This is one of the many examples that make me wish I was writing something more modern and cohesive like Clojure.
Of course there are certainly packages that provide structures with better ergonomics but to be honest I don't even know where to look. Like other Lisps, Elisp is extremely extensible but also like other Lisps it suffers from a certain degree of fragmentation, and perhaps due to its age there's likely a reluctance to "modernize" things when there's so much history behind it. At any rate, there's a lot of nice modern packages around (I've been using kubel as inspiration for my AWS exercise) so even if the documentation is uncomfortable to me there's plenty of source code to read. Hopefully I'll be able to move my little projects forward and learn some more of the editor I've been using for most of my life on the way.
More Godot rotations
It turns out that Godot's "Y is positive" convention originates in OpenGL, which follows the same convention.
I also learned that Direct3D uses a left-handed coordinate system (while still having y pointing up). It's weird but it also means that if you go forward you move in the positive Z direction, whereas in Godot you go negative. (The camera will point in the positive Z direction by default though.)
Finally, I enjoyed seeing Euler getting roasted in the Godot docs:
This way of representing 3D rotations was groundbreaking at the time, but it has several shortcomings when used in game development (which is to be expected from a guy with a funny hat).
I like this notion of having one of the most important mathematicians of all time be just a guy with a funny hat who came up with a system for rotations that happens to be not very good for games.
Go is kinda nice
Ever since I first heard about Go (before it even came out, I believe) I was very dismissive. It was a language for old crufty programmers who mainly wanted a slightly nicer C, ignoring all recent developments in language design. The future was in languages with super advanced compilers like Haskell. For people who couldn't handle Haskell there was always OCaml.
This was before I had any kind of software development job though, and since then I've been learning to appreciate the value of incremental changes as opposed to introducing a paradigm shift. Haskell and OCaml aren't much more popular than they were back in 2008 but a lot of ideas from functional programming and the Hindley-Miller type system have become more mainstream.
Go eschews even those developments but it's still doing fine. At least for the type of work that I tend to, which is mostly web servers. I might be missing some of the history here, but Go seems to have avoided the framework and library churn that I've observed throughout my career. Is it because a lot of the stuff you might need for a web server is already built-in, and most of it has been there from the start? But Python also has a lot built-in and it still went through a fair amount of churn. Maybe it's because people who are comfortable writing if (err != nil) { return nil, err }
are also not the kind of people to pursue the new hotness all the time.
Anyway, I've been writing and reading some Go for work and it's been good. Interfaces are pretty nice. I don't know yet whether I prefer them to Rust's traits, but so far I like that they're implicit. Writing an HTTP server is a simple matter of implementing a ServeHTTP(http.ResponseWriter, *http.Request)
method on a type. This will automatically make the type implement the Handler
interface, meaning I can just pass it to http.ListenAndServe()
. It's pretty sweet.
Bamboozled about Godot rotations
I've been messing around with Godot lately and one of the first things I learned is that, in 3D, Y is up. This conflicts with my experience in Blender and also with the convention I'm used to in mathematics; both have Z pointing up. Initially that seemed to be just a matter of getting used to mentally translate Y to Z and vice versa when handling with both systems, but once I started playing with rotations that turned out not to be enough. For reference here's how they look in Godot and Blender, respectively:


The thing is, doing Y<->Z led me to believe that they adopt different conventions for rotation, when they're in fact exactly the same. The easiest way to see that is by looking at the axes at the top right corner. Under this translation, to get from positive X to "Y" (actually Z) you'd rotate clockwise around "Z" (actually Y), whereas in Blender you go counter-clockwise. My confusion essentially turned Godot's system into a left-hand coordinate system, but in fact it's a right-hand system like Blender's: If you start from Blender's and rotate Z counter-clockwise around X by 90 degrees, you end up in Godot's sytem. (When I say counter-clockwise, I mean from the perspective of someone standing on positive X and looking from above. If they're looking from below, it'd look clockwise.)
It just looks misleading because Y is up in Godot. The poorly made video below shows the same rotation in both applications, from the same perspective, just taking the axes into account and ignoring the fact that the meaning of "up" is different. Note that both Blender and Godot treat the rotation as positive. This means that counter-clockwise rotations are positive in both.