multiple-value in single-value context em Go

O erro “multiple-value X() (untyped nil, error) in single-value context” aparece quando você tenta usar o resultado de uma função que retorna múltiplos valores em um contexto que espera apenas um valor. Em Go, funções frequentemente retornam dois ou mais valores (especialmente o padrão (valor, error)), e o compilador exige que você trate todos eles.


A Mensagem de Erro

./main.go:10:7: multiple-value os.Open() (untyped nil, error) used in single-value context
./main.go:15:12: multiple-value strconv.Atoi() (int, error) used in single-value context

Causas Comuns

1. Atribuição Simples de Função com Dois Retornos

O caso mais comum — tentar capturar o resultado em uma única variável:

package main

import "os"

func main() {
    // ERRO: os.Open retorna (*File, error)
    file := os.Open("config.json")
    _ = file
}

2. Usar Multi-Return Diretamente em Expressão

Tentar usar o retorno diretamente em uma chamada de função ou expressão:

package main

import (
    "fmt"
    "strconv"
)

func main() {
    // ERRO: strconv.Atoi retorna (int, error)
    fmt.Println(strconv.Atoi("42"))
}

Nota: Na verdade, fmt.Println aceita ...interface{}, então este caso específico compila! Mas outros contextos não:

package main

import "strconv"

func dobro(n int) int {
    return n * 2
}

func main() {
    // ERRO: strconv.Atoi retorna (int, error), dobro espera apenas int
    resultado := dobro(strconv.Atoi("42"))
    _ = resultado
}

3. Passando Multi-Return como Argumento

Funções que recebem um único argumento não aceitam multi-return:

package main

import (
    "os"
    "io"
)

func processar(r io.Reader) {
    // ...
}

func main() {
    // ERRO: os.Open retorna (*os.File, error)
    processar(os.Open("dados.txt"))
}

4. Atribuição em Struct Literal ou Map

Multi-return não funciona em inicializações inline:

package main

import "strconv"

type Config struct {
    Port int
}

func main() {
    // ERRO: strconv.Atoi retorna (int, error)
    cfg := Config{
        Port: strconv.Atoi("8080"),
    }
    _ = cfg
}

Como Resolver

Solução 1: Capturar Todos os Valores de Retorno

A forma idiomática em Go — capture tudo e trate o erro:

package main

import (
    "fmt"
    "os"
)

func main() {
    file, err := os.Open("config.json")
    if err != nil {
        fmt.Println("Erro:", err)
        return
    }
    defer file.Close()

    fmt.Println("Arquivo aberto:", file.Name())
}

Solução 2: Descartar Valores com _

Se você tem certeza de que não precisa de um valor, use o identificador em branco:

package main

import (
    "fmt"
    "strconv"
)

func main() {
    // Descarta o erro (CUIDADO: só faça isso quando for seguro!)
    valor, _ := strconv.Atoi("42")
    fmt.Println(valor) // 42
}

Aviso: Descartar erros com _ é geralmente uma má prática. Só faça quando o erro é logicamente impossível (como converter uma constante string conhecida).

Solução 3: Variável Intermediária

Quando precisa usar o resultado em uma expressão:

package main

import (
    "fmt"
    "strconv"
)

func dobro(n int) int {
    return n * 2
}

func main() {
    // Primeiro captura
    n, err := strconv.Atoi("42")
    if err != nil {
        fmt.Println("Erro:", err)
        return
    }

    // Depois usa
    resultado := dobro(n)
    fmt.Println(resultado) // 84
}

Solução 4: Criar Helper para Ignorar Erro

Para conversões em constantes ou inicializações de pacote, crie um helper must:

package main

import (
    "fmt"
    "strconv"
)

// must panics se err != nil — use apenas com valores constantes
func must[T any](val T, err error) T {
    if err != nil {
        panic(err)
    }
    return val
}

func main() {
    // Seguro: "8080" é uma constante válida
    port := must(strconv.Atoi("8080"))
    fmt.Println("Porta:", port)
}

A partir do Go 1.18 com generics, o pattern must funciona com qualquer tipo. Funções Must já existem na stdlib, como template.Must e regexp.MustCompile.

Solução 5: Inicialização em Duas Etapas

Para structs e maps:

package main

import (
    "fmt"
    "strconv"
)

type Config struct {
    Port int
}

func main() {
    port, err := strconv.Atoi("8080")
    if err != nil {
        fmt.Println("Porta inválida:", err)
        return
    }

    cfg := Config{
        Port: port,
    }
    fmt.Printf("Porta: %d\n", cfg.Port)
}

Por Que Go Faz Assim?

Go exige que você lide com todos os valores de retorno por design. Isso:

  1. Força o tratamento de erros — você não pode “esquecer” que uma função pode falhar
  2. Torna o fluxo explícito — cada possibilidade de falha é visível no código
  3. Evita bugs silenciosos — diferente de exceções, erros não são ignorados sem que você deliberadamente use _

Essa filosofia é detalhada em por que aprender Go e no guia de tratamento de erros.


Dicas para Evitar Este Erro

  1. Sempre capture todos os retornos — o padrão valor, err := funcao() é onipresente em Go.

  2. Trate erros imediatamente — use if err != nil logo após a chamada. Veja boas práticas de erros.

  3. Use must para constantes — crie helpers com generics para valores que sabidamente não falham.

  4. Configure o gopls — seu editor mostrará o erro antes de compilar.

  5. Nunca descarte erros em produção_, _ = funcao() esconde bugs. Use logging estruturado para registrar erros.


Erros Relacionados