{"record":{"id":"0b12e33bd4b1e30b","repo":"gohugoio/hugo","slug":"non-comparable-types-s-v-s-v","errorCode":null,"errorMessage":"non-comparable types %s: %v, %s: %v","messagePattern":"non-comparable types (.+?): (.+?), (.+?): (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tpl/internal/go_templates/texttemplate/funcs.go","lineNumber":499,"sourceCode":"\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tswitch k1 {\n\t\t\tcase boolKind:\n\t\t\t\ttruth = arg1.Bool() == arg.Bool()\n\t\t\tcase complexKind:\n\t\t\t\ttruth = arg1.Complex() == arg.Complex()\n\t\t\tcase floatKind:\n\t\t\t\ttruth = arg1.Float() == arg.Float()\n\t\t\tcase intKind:\n\t\t\t\ttruth = arg1.Int() == arg.Int()\n\t\t\tcase stringKind:\n\t\t\t\ttruth = arg1.String() == arg.String()\n\t\t\tcase uintKind:\n\t\t\t\ttruth = arg1.Uint() == arg.Uint()\n\t\t\tdefault:\n\t\t\t\tif !canCompare(arg1, arg) {\n\t\t\t\t\treturn false, fmt.Errorf(\"non-comparable types %s: %v, %s: %v\", arg1, arg1.Type(), arg.Type(), arg)\n\t\t\t\t}\n\t\t\t\tif isNil(arg1) || isNil(arg) {\n\t\t\t\t\ttruth = isNil(arg) == isNil(arg1)\n\t\t\t\t} else {\n\t\t\t\t\tif !arg.Type().Comparable() {\n\t\t\t\t\t\treturn false, fmt.Errorf(\"non-comparable type %s: %v\", arg, arg.Type())\n\t\t\t\t\t}\n\t\t\t\t\ttruth = arg1.Interface() == arg.Interface()\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif truth {\n\t\t\treturn true, nil\n\t\t}\n\t}\n\treturn false, nil\n}\n","sourceCodeStart":481,"sourceCodeEnd":517,"githubUrl":"https://github.com/gohugoio/hugo/blob/52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2/tpl/internal/go_templates/texttemplate/funcs.go#L481-L517","documentation":"Raised by `eq` when two operands share the same `basicKind` invalidKind fallback (i.e. neither is a basic scalar kind), they are not nil, and `canCompare` is false (funcs.go:498-499). This catches comparisons between struct/interface/pointer values of different dynamic types that the engine cannot equate. Format: `val1 type1 type2 val2`.","triggerScenarios":"`{{eq .A .B}}` where `.A` is a `*User` and `.B` is a `*Post`; comparing two struct values of different types; comparing a struct to a map.","commonSituations":"Comparing object references of different concrete types; refactoring types so two values that used to be comparable no longer are; loose equality expectations across heterogeneous data.","solutions":["Compare a stable identifier instead of whole objects: `{{eq .A.ID .B.ID}}`.","Ensure both operands are the same concrete type before comparing.","Implement an explicit equality helper FuncMap entry and call that."],"exampleFix":"// before\n{{eq .Author .Editor}}   // *User vs *Editor\n\n// after\n{{eq .Author.ID .Editor.ID}}","handlingStrategy":"type-guard","validationCode":"// Compare identifiers, not whole objects of different types:\n//   {{eq .Author.ID .Editor.ID}}","typeGuard":"func sameConcreteType(a, b interface{}) bool {\n    if a == nil || b == nil { return false }\n    return reflect.TypeOf(a) == reflect.TypeOf(b)\n}","tryCatchPattern":null,"preventionTips":["Compare a stable ID rather than heterogeneous structs.","Ensure both operands share a concrete type before eq.","Expose a typed equality helper for complex types."],"tags":["template","comparison","eq","reflection","text-template"],"backgroundTag":null,"analyzedSha":"52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2","analyzedAt":"2026-08-09T21:49:36.660Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}