Skip to content
gohacks.dev
Basics

Invalid Operation Mismatched Types Error in Go Fixed

Go will not mix int and float64 or other numeric types in one expression. See the message in six forms and decide which side to convert, and when not to.

6 min read
Summarize in AI
Flat illustration of an editor window full of code.

Of the compile errors a Go beginner meets, this is the one that feels most like the language being pedantic, and the one that most clearly prevents a real class of bugs. 3 * 2.5 works in Go, and count * price does not, when count is an int variable and price is a float64 variable. The difference is that one is two constants that Go can reconcile and the other is two typed values that it cannot. This article shows the six forms the error takes with the compiler's output, gives the fix for each and says how to choose the direction of the conversion. It is the companion to cannot use value as type, uses the rules in type conversion, and sits in the Go basics path.

Tested with Go 1.25.6, macOS on Apple Silicon (arm64)

Six forms in one file

main.goGo
package main

import "fmt"

type Celsius float64

func main() {
	count := 3
	price := 2.5
	fmt.Println(count * price)

	var a int32 = 1
	var b int64 = 2
	fmt.Println(a + b)

	var c Celsius = 1
	fmt.Println(c + price)

	s := "n=" + count

	var u uint = 1
	fmt.Println(u + count)
	fmt.Println(count / 2.5)
	fmt.Println(count == 2.5)
	fmt.Println(s)
}
Text
# example.test/lab
./main.go:10:14: invalid operation: count * price (mismatched types int and float64)
./main.go:14:14: invalid operation: a + b (mismatched types int32 and int64)
./main.go:17:14: invalid operation: c + price (mismatched types Celsius and float64)
./main.go:19:7: invalid operation: "n=" + count (mismatched types untyped string and int)
./main.go:22:14: invalid operation: u + count (mismatched types uint and int)
./main.go:23:22: 2.5 (untyped float constant) truncated to int
./main.go:24:23: 2.5 (untyped float constant) truncated to int

The messages follow one pattern, invalid operation: <expression> (mismatched types X and Y), and the last two lines are a different message that points at a constant. In order:

count * price: int and float64. The usual one. Each type has its own arithmetic, so Go will not guess whether you meant integer or floating point multiplication, and the two give different answers.

a + b: int32 and int64. Even integer types of different sizes do not mix, since the result type would be ambiguous. This surprises people who expect "the wider one wins". In Go no one wins until you write the conversion.

c + price: Celsius and float64. A named type is its own type, even with the same underlying representation. That is why defining type Celsius float64 protects you from adding a temperature to a price.

"n=" + count: the message says untyped string and int. The + for strings concatenates, and it needs two strings. Go never converts a number to text as part of an operator.

u + count: uint and int. Unsigned and signed types do not mix either, and the usual cause is a len() result, which is an int, being combined with a value that came from a uint API.

count / 2.5 and count == 2.5: these two say 2.5 (untyped float constant) truncated to int. The constant 2.5 was converted to the type of the other operand, int, and it does not fit, since an int has no fractional part. This is the rule that lets price * 2 work for a float price while rejecting count / 2.5. If you see "truncated", the problem is the constant, and the question is whether count should be a float.

Fixes, one at a time

main.goGo
package main

import (
	"fmt"
	"strconv"
)

type Celsius float64

func main() {
	count := 3
	price := 2.5
	fmt.Println(float64(count) * price)

	var a int32 = 1
	var b int64 = 2
	fmt.Println(int64(a) + b)

	var c Celsius = 1
	fmt.Println(float64(c)+price, c+Celsius(price))

	fmt.Println("n=" + strconv.Itoa(count))
	fmt.Println(fmt.Sprintf("n=%d", count))

	var u uint = 1
	fmt.Println(int(u) + count)
	fmt.Println(float64(count) / 2.5)
}
Text
7.5
3
3.5 3.5
n=3
n=3
4
1.2

Each fix converts one operand so both match. For count * price, converting count to float64 is lossless for any reasonable count. For a + b, widening to int64 is safe. For c + price there are two reasonable answers, to drop to float64 or to lift price into Celsius, and the second keeps the result in the domain type, which is usually what you want. For text, strconv.Itoa is fast and fmt.Sprintf handles other formats, and the fix is never a conversion like string(count), which yields a character, as type conversion explains. The unsigned case converts u to int, and that is safe only if u fits, which is where integer overflow bites.

Choosing the direction

The conversion has a direction, and the wrong direction loses information without any error. Three rules cover it.

Widen, never narrow, when you can. Convert int to float64, int32 to int64, uint8 to int. The reverse truncates or wraps. int(price) * count compiles and silently throws away the cents.

Convert the value that is least trusted, closest to its source. If a uint arrives from a protocol and the rest of the program uses int, convert once, where it enters, after checking the range, and keep the rest of the code in one type. Scattered conversions in the middle of arithmetic are the sign of two types that should be one.

Do not use float for money or counts. If the fix you are about to write turns an int into a float64 to multiply by a price, pause. Prices in float64 accumulate rounding error, as why 0.1 plus 0.2 is not 0.3 shows, and amounts belong in integer cents. In that case convert the other way, by representing the price in cents, and the types match without any float.

Constants adapt, variables do not

The difference between price * 2 and price * n for an int variable n is the whole story. An untyped constant has no fixed type until it is used, so it takes the type of the other operand if the value fits. A variable has a type and keeps it. The same rule explains why time.Second * 5 compiles and time.Second * seconds does not, where seconds is an int variable. The fix is time.Second * time.Duration(seconds). The idea is in typed and untyped constants. It also explains why a loop for i := 0; i < 10; i++ can use float64(i) * 0.5 with one conversion in the right place.

How to find the real problem

If you convert and the compiler is happy, that does not mean the code is right. After fixing the error, ask what the types were telling you. A function that takes int and float64 and multiplies them might be better with both as float64, or with a named type for the unit. A struct with an int32 field and an int64 field that are always added together probably wants one type. Two packages that disagree on the integer width of the same concept, uint32 in one and int in the other, will give you this error forever until one of them changes. The error is the compiler pointing at a seam in your design, and the conversion is a stitch you choose to put there. When the types are fine and the operation really is mixed, the conversion is the right fix and it should be as close to the operator as possible so a reader sees what happens.

Versions

The wording of these messages has shifted between releases, the truncated form most of all, so match on the structure and not the exact text. The mechanism has not changed since Go 1.0. The language rule is in the specification on arithmetic operators: operands must have identical types unless the operation is a shift, and the section on constant expressions explains how untyped constants adapt.

Taggederror fix
Share

Keep reading

Get new articles by email

We send one email when a new article is published. No spam.

Unsubscribe in one click. No spam, ever.