In Go, what happens when an integer is divided by zero, at compile time and at run time?
answer
- the divisor decides when you find out
- a constant zero never reaches run time
- runtime.Error, not a fatal crash
- a deferred recover can convert it
basics
~20 sA constant zero divisor is rejected by the compiler. A divisor that is zero only at run time causes a panic carrying a runtime.Error with the message 'integer divide by zero', which a deferred recover in the same goroutine can catch.
solid answer
~50 sGo splits the two cases. If the divisor is a constant zero — `x / 0` or `x % 0` — the compiler rejects the program outright with `invalid operation: division by zero`, even in code that would never execute. If the divisor is a variable that happens to hold zero, the compiler cannot know, so the operation panics at run time with `runtime error: integer divide by zero`. That panic value implements `runtime.Error`, so it is an ordinary recoverable panic: a `recover()` inside a function deferred by the same goroutine catches it, and the usual pattern is to convert it into an `error` on a named result. The `%` operator panics identically for a zero divisor. Unrecovered, the panic prints a stack trace and terminates the whole process, not just the goroutine that divided.
code
go · 5 linesd := 0
fmt.Println(10 / d) // panic: runtime error: integer divide by zero
fmt.Println(10 % d) // the remainder operator panics the same way
// _ = 10 / 0 // does not compile: invalid operation: division by zerogo deeper
Know that dividing an integer by zero in Go is a panic, not a silent zero or a special value, and that writing a literal zero divisor stops the build before you ever run it.
Explain the split precisely: constant divisor equals compile error, variable divisor equals run-time panic with a runtime.Error, and % behaves the same. Show the deferred recover that converts it to an error on a named result.
Argue for prevention over recovery, and describe how you would trace it in production: the panic's stack trace names the division, not the source of the zero, so validation belongs where the divisor is computed.
Decide the policy — where in a service a recover boundary belongs, what it is allowed to swallow, and how an operator distinguishes a recovered arithmetic fault from an ordinary handled error in the logs.
## Two different failures, decided by one thing: is the divisor a constant? Go's specification says the divisor of a **constant** division or remainder operation must not be zero, and that if the divisor is zero **at run time**, a run-time panic occurs. So the same source-level mistake produces two very different experiences depending on how the zero got there. ### Compile time ```go _ = 10 / 0 // invalid operation: division by zero const n = 0 _ = 10 / n // invalid operation: division by zero ``` This is a hard compile error, and reachability is irrelevant — a constant expression is evaluated where it is written, so putting it behind `if false` does not help. That is a small but genuine safety win: a whole class of typo is caught before the binary exists. ### Run time ```go d := 0 fmt.Println(10 / d) // panic: runtime error: integer divide by zero ``` The compiler will not fold `d`, so the check happens on the hardware and the runtime turns the resulting fault into a Go panic. Note that this is a real Go panic, not an OS-level crash: it unwinds deferred functions and prints a Go stack trace. `%` behaves identically. `10 % d` with `d == 0` panics with exactly the same message, which surprises people who think of the remainder as cheaper or safer than the division. ## The panic value The value handed to `recover()` is a `runtime.Error` — an interface that embeds `error`, so it has an `Error() string` method, and its message is `runtime error: integer divide by zero`. Because it is an ordinary panic value, all of the normal rules apply: - A deferred function in the **same goroutine** can call `recover()` and stop the unwinding. - `recover()` only works when called directly by a deferred function of the panicking goroutine. - If nobody recovers, the program exits with a non-zero status after printing the stack trace, no matter which goroutine divided. This is worth stating explicitly because a chunk of runtime failures are **not** recoverable — a concurrent map write and a stack overflow, for instance, are fatal errors that bypass `recover()` entirely. Divide by zero is not in that category. ```go func safeDiv(a, b int) (q int, err error) { defer func() { if r := recover(); r != nil { err = fmt.Errorf("division failed: %v", r) } }() return a / b, nil } ``` When `b` is zero, the panic fires before the return values are set, the deferred closure assigns the **named** result `err`, and the caller sees `q == 0` with a non-nil error. ## Should you recover, though? Usually not. A zero divisor is nearly always a logic error you should have prevented, and Go's culture is to check the precondition rather than to catch the fault: ```go if len(shards) == 0 { return nil, errors.New("no shards configured") } shard := key % len(shards) ``` That reads better than a `recover`, keeps the error path explicit, and produces a message that names the actual problem instead of a hardware fault. The place where recovery genuinely earns its keep is a boundary that must not take the whole process down — a request handler, a plugin host, a worker loop draining a queue — where a bad input should fail one unit of work rather than terminate the service. ## Diagnosing it after the fact When this reaches production, what you have is the panic's stack trace. It names the goroutine, the file and line of the division, and the frames below it. Two things make the trace less useful than it looks: - The trace points at the **division**, not at where the zero came from. A `len()` of an empty slice, a config value that defaulted, an unparsed flag — the origin is often several frames up. - If the divisor arrived from data rather than from configuration, it will be intermittent: the build fails only on the runs whose input happens to include the empty case, which is exactly the shape of a flaky CI job. The durable fix is to validate the divisor once, at the edge where it is computed, and to make the zero case a typed error rather than something the arithmetic discovers. ## Quick summary | divisor | when you find out | what you get | |---|---|---| | constant `0` | compile time | `invalid operation: division by zero` | | variable holding `0` | run time | panic, `runtime error: integer divide by zero` | | variable holding `0`, with `%` | run time | the same panic | | variable holding `0`, inside a deferred `recover` | run time | a `runtime.Error` you can convert to an `error` |
- Is an integer divide-by-zero panic recoverable, and how does that differ from a concurrent map write?It is recoverable: the panic value implements `runtime.Error`, and a `recover()` inside a function deferred by the same goroutine stops the unwinding. A concurrent map write is different — the runtime reports it as a fatal error that does not run deferred functions and cannot be recovered, because the map's state is already corrupt. Do not assume every runtime failure behaves like divide by zero.
- Would you recover from this in a service, or prevent it?Prevent it. Check the divisor where it is produced — an empty shard list, an unset config value — and return a typed error that names the real problem. A blanket recover at a request boundary is still worth having so one bad request cannot kill the process, but it is a backstop, not the fix: the recovered message says 'integer divide by zero', which tells an on-call engineer nothing about which value was empty.
- Does the compiler reject x / y when it can see y is always zero?Only when `y` is a constant. If `y` is a variable the operation is compiled even if a human can see it is always zero, because the language rule is about constant expressions, not about optimiser reasoning. That keeps the rule predictable across compiler versions: whether your program builds must not depend on how clever the constant propagation happens to be.
saying these in an interview costs you the question
- Says integer division by zero returns zero or an infinite value
- Claims the compiler catches every zero divisor
- Thinks the divide-by-zero panic cannot be recovered
- Forgets that % by a zero divisor panics identically
- Believes the panic kills only the offending goroutine