Part III · Making It Solid
Chapter 10: When Things Go Wrong
Try something with your chapter 9 code: at the chest, instead of typing a
number, type abc. Your game crashes. Not "the dial refuses," not "cold,"
an actual runtime error, and the program stops dead. Your player didn't do
anything malicious, they just typed the wrong kind of thing, and your
game had no way to recover. This chapter fixes that.
Why It Crashes
The line at fault is one you already know well:
var guess = num(input("Turn the dial to a number (1-100): "))num() can convert a lot of things, but it can't convert "abc" into a
number, there's no reasonable number that text could mean. When that
happens, QF Code raises a runtime error, and unless something in your
program is watching for it, that error stops the whole program right
there.
attempt and error
attempt lets you run code that might fail, and catch the failure
instead of letting it end your program.
attempt
var guess = num(input("Turn the dial to a number (1-100): "))
error
writeln("That doesn't look like a number.")
end attemptIf the line inside attempt succeeds, error never runs, execution just
continues normally after end attempt. If it fails, QF Code jumps
straight to error instead of crashing.
Capturing the Message
You can also grab the actual error message QF Code generated, by naming a
variable right after error:
attempt
var guess = num(input("Turn the dial to a number (1-100): "))
error message
writeln("Problem: " + message)
end attemptThat variable only exists inside the handler, and it holds a human-readable description of what went wrong, useful for debugging while you're building, less useful to show a player directly in a finished game.
signal
Sometimes you want to raise an error on purpose, because your own code detected a problem, not because a built-in function failed.
function checkAge(age)
if age < 0 then
signal("Age cannot be negative.")
end if
return age
end functionsignal() raises a real QF Code runtime error with whatever message you
give it, and an attempt elsewhere can catch it exactly the same way it
would catch any other runtime error.
last_error
last_error is always available, and holds three pieces of information
about the most recent caught error: last_error.message,
last_error.line, and last_error.code. For most situations, naming a
variable after error is simpler and does the same job. last_error is
there for the cases where you need it, particularly if you want to check
error details somewhere other than immediately inside the handler that
caught it.
Fixing the Chest
Now the dial stops crashing on bad input, and just asks again instead.
var guess = empty
var validInput = false
while not validInput
attempt
guess = num(input("Turn the dial to a number (1-100): "))
validInput = true
error
writeln("That doesn't look like a number. Try again.")
end attempt
end while
guessesLeft = guessesLeft - 1A guess only counts against guessesLeft once it's actually a valid
number. Typing abc costs the player nothing but a moment, instead of
costing them the whole game.
Make It Yours. Find one other spot in your game where bad input could
break something, maybe the name prompt, maybe a command, and wrap it in
an attempt of your own. It's fine if nothing there is actually broken
yet, the practice of asking "what could go wrong here" is the real point.
Chapter Checkpoint
Open either known-good checkpoint directly in QF Code, or download the .qfc file to keep locally. The IDE opens in a new tab and does not run the program automatically.
Start this chapter
Chapter 10 starting checkpoint.
Continue from here
Chapter 10 completed checkpoint.
Starting code: the Chapter 9 checkpoint.
Completed code (Chapter 10):
// The Hollow Cottage
// Chapter 10
const cottageName = "The Hollow Cottage"
function showInventory(items)
writeln("You are carrying:")
for each item in items
writeln("- " + item)
end for
end function
writeln("You are standing outside " + cottageName + ".")
var playerName = input("What is your name, traveler? ")
writeln("Welcome, " + playerName + ".")
var tries = 0
var solved = false
while tries < 3 and not solved
tries = tries + 1
var answer = input("There is a riddle carved into the door. It asks: what has keys but no locks? ")
if lower(trim(answer)) == "piano" then
writeln("The door swings open. You step inside.")
solved = true
else
writeln("Nothing happens.")
end if
end while
if not solved then
writeln("The door refuses to budge. You'll have to find another way in.")
else
writeln("You step inside. In the corner sits a locked chest with a dial numbered 1 to 100.")
var secret = randomint(1, 100)
var guessesLeft = 6
var found = false
while guessesLeft > 0 and not found
var guess = empty
var validInput = false
while not validInput
attempt
guess = num(input("Turn the dial to a number (1-100): "))
validInput = true
error
writeln("That doesn't look like a number. Try again.")
end attempt
end while
guessesLeft = guessesLeft - 1
if guess == secret then
writeln("The chest clicks open!")
found = true
else
var distance = abs(guess - secret)
match distance
when 0 to 5
writeln("Very warm. " + guessesLeft + " turns left.")
when 6 to 20
writeln("Warm. " + guessesLeft + " turns left.")
when else
writeln("Cold. " + guessesLeft + " turns left.")
end match
end if
end while
var inventory = array()
append(inventory, "rusty key")
append(inventory, "half-burned candle")
if not found then
writeln("The dial jams. The chest stays locked, for now.")
else
append(inventory, "small silver coin")
writeln("Inside, you find a small silver coin.")
showInventory(inventory)
end if
var command = input("What do you do? ")
var words = split(lower(trim(command)), " ")
var verb = words[0]
match verb
when "take"
showInventory(inventory)
when "look"
writeln("A small cottage, one room, one door out.")
when "quit"
when "exit"
writeln("You step back out into the cold.")
when else
writeln("Nothing happens.")
end match
end ifCopyright © 2026 Edison Mooers. Published by Gibidda Press. Free for personal learning and qualifying noncommercial educational use. Commercial training, organizational use, redistribution, and adaptation require written permission. Full use terms.