C# Exceções

Uma exceção é o jeito de o .NET dizer "não consigo continuar": o índice não existe, o texto não é número, o arquivo sumiu. Se ninguém trata, o programa para e mostra a mensagem. Esta página lista as exceções que você mais vai ver, cada uma com o código que a provoca, a mensagem real, o que ela quer dizer e como evitar. Para aprender com calma, veja Exceções, try e catch e throw.

Como usar esta página
  • Copie um pedaço da mensagem que apareceu para você (por exemplo, was not in a correct format) e procure com Ctrl+F.
  • No Linux, as mensagens das exceções saem em inglês, mesmo com o sistema em português (o .NET não traz a tradução delas). As do compilador (CSxxxx) saem em português: estão em Erros do Compilador.
  • Todas foram provocadas de verdade, no dotnet 10.0.112 da máquina da Ana (pasta ~/cs/exc), e conferidas no editor do guia. Onde o editor mostra outra coisa, a tabela diz.

Atalhos: Ler uma exceção · As exceções mais comuns · A família · O que tem dentro · Tratar · Lançar · Evitar em vez de tratar · Experimente


Como ler uma exceção não tratada

int[] notas = [7, 9, 5];
Console.WriteLine("Antes");
Console.WriteLine(notas[3]);
Console.WriteLine("Depois");
$ dotnet run naotratada.cs
Antes
Unhandled exception. System.IndexOutOfRangeException: Index was outside the bounds of the array.
   at Program.<Main>$(String[] args) in /home/ana/cs/exc/naotratada.cs:line 3
$ echo $?
134
PedaçoO que quer dizer
Unhandled exception."Exceção não tratada": nenhum catch pegou, e o programa parou ali. O Depois nunca apareceu.
System.IndexOutOfRangeExceptionO tipo da exceção, com o namespace. É o que você escreve num catch.
Index was outside the bounds of the array.A mensagem (a propriedade Message): "o índice estava fora dos limites do array".
at Program.<Main>$(String[] args)O rastro (stack trace): os métodos em andamento, do mais recente (em cima) para o mais antigo. <Main>$ é o nome que o compilador dá às instruções de nível superior.
in /home/ana/cs/exc/naotratada.cs:line 3Arquivo e linha. Leia o rastro de cima para baixo até achar o primeiro arquivo seu: as linhas de System.… são de dentro do .NET.
134O código de saída de um programa que caiu por exceção, no Linux (é o sinal SIGABRT: 128 + 6). Um programa que termina bem sai com 0.

Este programa provoca 30 exceções, uma por linha, e mostra o tipo e a mensagem de cada uma. Antes de rodar, a Ana criou um arquivo só de leitura, para a linha 20:

// catalogo.cs — provoca cada exceção e mostra o tipo e a mensagem
using System.Collections;

string? nulo = null;
int[] v = [1, 2, 3];
List<int> lista = [1, 2, 3];
Dictionary<string, int> d = new() { ["a"] = 1 };
object texto = "x";
int zero = 0;
int? semValor = null;
string grande = "99999999999";
string titulo = "  ";
int quantidade = -5;

Provocar(1, () => _ = nulo!.Length);
Provocar(2, () => _ = v[3]);
Provocar(3, () => _ = lista[3]);
Provocar(4, () => _ = d["b"]);
Provocar(5, () => d.Add("a", 2));
Provocar(6, () => _ = int.Parse("abc"));
Provocar(7, () => _ = int.Parse(grande));
Provocar(8, () => _ = checked(int.MaxValue + zero + 1));
Provocar(9, () => _ = 10 / zero);
Provocar(10, () => _ = (int)texto);
Provocar(11, () => _ = new List<int>().First());
Provocar(12, () => { foreach (int x in lista) lista.Add(x); });
Provocar(13, () => _ = semValor!.Value);
Provocar(14, () => _ = int.Parse(nulo!));
Provocar(15, () => ArgumentNullException.ThrowIfNull(nulo));
Provocar(16, () => ArgumentException.ThrowIfNullOrWhiteSpace(titulo));
Provocar(17, () => ArgumentOutOfRangeException.ThrowIfNegative(quantidade));
Provocar(18, () => _ = File.ReadAllText("nao-existe.txt"));
Provocar(19, () => _ = File.ReadAllText("pasta-que-nao-existe/a.txt"));
Provocar(20, () => File.WriteAllText("somente-leitura.txt", "oi"));
Provocar(21, () => ((IList)v).Add(4));
Provocar(22, () => { var w = new StringWriter(); w.Dispose(); w.Write("oi"); });
Provocar(23, () => throw new NotImplementedException());
Provocar(24, () => _ = "Ana".Substring(5));
Provocar(25, () => _ = new string('x', -1));
Provocar(26, () => _ = DateTime.Parse("31/02/2026"));
Provocar(27, () => _ = Enum.Parse<DayOfWeek>("Segunda"));
Provocar(28, () => { var c = new CancellationTokenSource(); c.Cancel(); c.Token.ThrowIfCancellationRequested(); });
Provocar(29, () => _ = decimal.Parse("1,5,0"));
Provocar(30, () => _ = Convert.ToInt32("12a"));

static void Provocar(int n, Action acao)
{
    try
    {
        acao();
        Console.WriteLine($"{n,2} (nenhuma exceção)");
    }
    catch (Exception e)
    {
        Console.WriteLine($"{n,2} {e.GetType().FullName}: {e.Message}");
    }
}
$ touch somente-leitura.txt
$ chmod 444 somente-leitura.txt
$ dotnet run catalogo.cs
 1 System.NullReferenceException: Object reference not set to an instance of an object.
 2 System.IndexOutOfRangeException: Index was outside the bounds of the array.
 3 System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index')
 4 System.Collections.Generic.KeyNotFoundException: The given key 'b' was not present in the dictionary.
 5 System.ArgumentException: An item with the same key has already been added. Key: a
 6 System.FormatException: The input string 'abc' was not in a correct format.
 7 System.OverflowException: Value was either too large or too small for an Int32.
 8 System.OverflowException: Arithmetic operation resulted in an overflow.
 9 System.DivideByZeroException: Attempted to divide by zero.
10 System.InvalidCastException: Unable to cast object of type 'System.String' to type 'System.Int32'.
11 System.InvalidOperationException: Sequence contains no elements
12 System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
13 System.InvalidOperationException: Nullable object must have a value.
14 System.ArgumentNullException: Value cannot be null. (Parameter 's')
15 System.ArgumentNullException: Value cannot be null. (Parameter 'nulo')
16 System.ArgumentException: The value cannot be an empty string or composed entirely of whitespace. (Parameter 'titulo')
17 System.ArgumentOutOfRangeException: quantidade ('-5') must be a non-negative value. (Parameter 'quantidade')
Actual value was -5.
18 System.IO.FileNotFoundException: Could not find file '/home/ana/cs/exc/nao-existe.txt'.
19 System.IO.DirectoryNotFoundException: Could not find a part of the path '/home/ana/cs/exc/pasta-que-nao-existe/a.txt'.
20 System.UnauthorizedAccessException: Access to the path '/home/ana/cs/exc/somente-leitura.txt' is denied.
21 System.NotSupportedException: Collection was of a fixed size.
22 System.ObjectDisposedException: Cannot write to a closed TextWriter.
23 System.NotImplementedException: The method or operation is not implemented.
24 System.ArgumentOutOfRangeException: startIndex cannot be larger than length of string. (Parameter 'startIndex')
25 System.ArgumentOutOfRangeException: count ('-1') must be a non-negative value. (Parameter 'count')
Actual value was -1.
26 System.FormatException: String '31/02/2026' was not recognized as a valid DateTime.
27 System.ArgumentException: Requested value 'Segunda' was not found.
28 System.OperationCanceledException: The operation was canceled.
29 System.FormatException: The input string '1,5,0' was not in a correct format.
30 System.FormatException: The input string '12a' was not in a correct format.

Dados e valores

#ExceçãoQuando aconteceMensagem realComo evitarAula
1NullReferenceExceptionusar . num valor nullObject reference not set to an instance of an object.confira is null, use ?. e ??, e leve a sério o aviso CS8602Referências Anuláveis
13InvalidOperationException.Value de um int? sem valorNullable object must have a value.HasValue, ?? padrão ou GetValueOrDefault()null
6FormatExceptionint.Parse("abc")The input string 'abc' was not in a correct format.int.TryParse com o que o usuário digitaParse e TryParse
29, 30FormatExceptiondecimal.Parse("1,5,0"), Convert.ToInt32("12a")The input string '1,5,0' was not in a correct format.decimal.TryParse; atenção à cultura (vírgula ou ponto)Parse e TryParse
26FormatExceptionDateTime.Parse("31/02/2026")String '31/02/2026' was not recognized as a valid DateTime.DateTime.TryParse (fevereiro não tem dia 31)Datas e Horas
7OverflowExceptionint.Parse("99999999999")Value was either too large or too small for an Int32.TryParse (devolve false), ou longInteiros
8OverflowExceptionconta que estoura dentro de checkedArithmetic operation resulted in an overflow.um tipo maior (long, decimal)Tipos e Limites
9DivideByZeroExceptioninteiro dividido por zeroAttempted to divide by zero.confira o divisor antes (com double, não há exceção: dá ∞)Operadores Aritméticos
10InvalidCastException(int)texto com object texto = "x"lab: Unable to cast object of type 'System.String' to type 'System.Int32'.
editor: Specified cast is not valid.
teste com is (if (o is int n)) ou use asPattern Matching
27ArgumentExceptionEnum.Parse<DayOfWeek>("Segunda")Requested value 'Segunda' was not found.Enum.TryParseEnums

Arrays, listas, textos e coleções

#ExceçãoQuando aconteceMensagem realComo evitarAula
2IndexOutOfRangeExceptionv[3] num array de 3 (ou "Ana"[3])Index was outside the bounds of the array.as posições vão de 0 a Length - 1; percorra com foreachArrays
3ArgumentOutOfRangeExceptionlista[3] numa List de 3Index was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index')de 0 a Count - 1List<T>
24ArgumentOutOfRangeException"Ana".Substring(5)startIndex cannot be larger than length of string. (Parameter 'startIndex')confira o LengthMétodos de String
25ArgumentOutOfRangeExceptionnew string('x', -1)count ('-1') must be a non-negative value. (Parameter 'count') Actual value was -1.não passe tamanhos negativosstring
4KeyNotFoundExceptiond["b"] com a chave ausenteThe given key 'b' was not present in the dictionary.TryGetValue ou GetValueOrDefaultDictionary
5ArgumentExceptiond.Add("a", 2) com a chave já existenteAn item with the same key has already been added. Key: ad["a"] = 2 (troca) ou TryAddDictionary
11InvalidOperationExceptionFirst() numa sequência vaziaSequence contains no elementsFirstOrDefault, ou Any() antesLINQ
12InvalidOperationExceptionmudar a lista dentro do foreach delaCollection was modified; enumeration operation may not execute.RemoveAll, ou percorra uma cópia (.ToList())foreach
21NotSupportedExceptionAdd num array (pela interface IList)Collection was of a fixed size.array tem tamanho fixo: use ListArrays

Outras de coleções e LINQ, com a mensagem: Queue empty., Stack empty., Sequence contains no matching element, Sequence contains more than one element (em Coleções e LINQ).

Argumentos (lançadas pelos seus métodos)

#ExceçãoQuando aconteceMensagem realAula
14ArgumentNullExceptionint.Parse(null)Value cannot be null. (Parameter 's')Parse e TryParse
15ArgumentNullExceptionArgumentNullException.ThrowIfNull(nulo) .NET 6Value cannot be null. (Parameter 'nulo')throw
16ArgumentExceptionArgumentException.ThrowIfNullOrWhiteSpace(titulo) .NET 8The value cannot be an empty string or composed entirely of whitespace. (Parameter 'titulo')throw
17ArgumentOutOfRangeExceptionArgumentOutOfRangeException.ThrowIfNegative(quantidade) .NET 8quantidade ('-5') must be a non-negative value. (Parameter 'quantidade') Actual value was -5.throw

Os ThrowIf… descobrem sozinhos o nome da variável (o (Parameter 'titulo')). Há também ThrowIfNullOrEmpty, ThrowIfZero, ThrowIfNegativeOrZero, ThrowIfGreaterThan e ThrowIfLessThan.

Arquivos e recursos

#ExceçãoQuando aconteceMensagem realComo evitarAula
18FileNotFoundExceptionler um arquivo que não existeCould not find file '/home/ana/cs/exc/nao-existe.txt'.File.Exists antes; a mensagem mostra o caminho completo em que o programa procurouArquivos
19DirectoryNotFoundExceptiona pasta do caminho não existeCould not find a part of the path '/home/ana/cs/exc/pasta-que-nao-existe/a.txt'.Directory.CreateDirectory antes de gravarPastas e Caminhos
20UnauthorizedAccessExceptiongravar num arquivo sem permissãoAccess to the path '/home/ana/cs/exc/somente-leitura.txt' is denied.grave na pasta da usuária; trate com try/catchArquivos
22ObjectDisposedExceptionusar um StreamWriter depois de fechadoCannot write to a closed TextWriter.use o objeto só dentro do bloco do usingusing e IDisposable
28OperationCanceledExceptionuma tarefa cancelada com CancellationTokenThe operation was canceled.trate no catch: em geral, é o usuário que cancelouCancelamento
23NotImplementedExceptionthrow new NotImplementedException(): "ainda não escrevi isto"The method or operation is not implemented.escreva o métodoInterfaces

Todas as exceções de arquivo descendem de IOException: um catch (IOException) pega todas (menos a UnauthorizedAccessException, que é de outra família).

Stack overflow: a que não dá para tratar

static int Contar(int n) => Contar(n + 1) + 1;
Console.WriteLine(Contar(0));
$ dotnet run recursao.cs
Stack overflow.
Repeated 261726 times:
--------------------------------
   at Program.<<Main>$>g__Contar|0_0(Int32)
--------------------------------
   at Program.<Main>$(System.String[])
$ echo $?
134

Uma recursão sem caso de parada enche a pilha de chamadas. O .NET encerra o programa na hora: nenhum catch pega uma StackOverflowException. O Repeated 261726 times é quantas vezes o Contar chegou a se chamar; o número muda um pouco de uma execução para outra (numa segunda vez, deu 261831). O editor do guia mostra a mesma mensagem e o mesmo código 134, mas com uma pilha menor: Repeated 999 times. Como consertar: toda recursão precisa de um caso que para sem chamar de novo.


A família das exceções

Toda exceção é uma classe que herda de Exception. Um catch pega a classe que você escreveu e todas as filhas:

Exception
├── SystemException
│   ├── ArgumentException
│   │   ├── ArgumentNullException
│   │   └── ArgumentOutOfRangeException
│   ├── ArithmeticException
│   │   ├── DivideByZeroException
│   │   └── OverflowException
│   ├── FormatException
│   ├── IndexOutOfRangeException
│   ├── InvalidCastException
│   ├── InvalidOperationException
│   │   └── ObjectDisposedException
│   ├── IO.IOException
│   │   ├── IO.FileNotFoundException
│   │   └── IO.DirectoryNotFoundException
│   ├── Collections.Generic.KeyNotFoundException
│   ├── NotSupportedException
│   ├── NotImplementedException
│   ├── NullReferenceException
│   ├── OperationCanceledException
│   └── UnauthorizedAccessException
└── (as suas: class SaldoInsuficienteException : Exception)

Por isso, num try com vários catch, a mais específica vem primeiro: catch (FileNotFoundException) antes de catch (IOException), e catch (Exception) por último. Ao contrário, o compilador reclama com o erro CS0160 (veja Erros do Compilador). Para criar a sua, aula Exceções Próprias.

O que tem dentro de uma exceção

// propriedades.cs — o que um objeto Exception tem dentro
try
{
    Carregar("config.txt");
}
catch (Exception e)
{
    Console.WriteLine($"Tipo: {e.GetType().Name}");
    Console.WriteLine($"Message: {e.Message}");
    Console.WriteLine($"InnerException: {e.InnerException?.GetType().Name}");
    Console.WriteLine($"InnerException.Message: {e.InnerException?.Message}");
    Console.WriteLine("StackTrace:");
    Console.WriteLine(e.StackTrace);
}

static void Carregar(string arquivo)
{
    try
    {
        File.ReadAllText(arquivo);
    }
    catch (FileNotFoundException e)
    {
        throw new InvalidOperationException($"Não consegui carregar {arquivo}.", e);
    }
}
$ dotnet run propriedades.cs
Tipo: InvalidOperationException
Message: Não consegui carregar config.txt.
InnerException: FileNotFoundException
InnerException.Message: Could not find file '/home/ana/cs/exc/config.txt'.
StackTrace:
   at Program.<<Main>$>g__Carregar|0_0(String arquivo) in /home/ana/cs/exc/propriedades.cs:line 24
   at Program.<Main>$(String[] args) in /home/ana/cs/exc/propriedades.cs:line 4
MembroO que é
Messagea mensagem, para pessoas
GetType().Nameo tipo (FileNotFoundException)
StackTraceo rastro, como texto
InnerExceptiona exceção original, quando uma exceção foi "embrulhada" em outra (o segundo argumento do construtor, como no Carregar)
ToString()tudo junto: tipo, mensagem, exceção interna e rastro
ParamNamesó em ArgumentException e filhas: o nome do parâmetro culpado
FileNamesó em FileNotFoundException: o caminho do arquivo

O nome estranho <<Main>$>g__Carregar|0_0 é como o compilador chama uma função local das instruções de nível superior. Leia só o Carregar.

Tratar: try, catch, when e finally

// trycatch.cs — tratar, filtrar, finally e relançar
foreach (string entrada in new[] { "10", "zero", "0", "99999999999" })
{
    try
    {
        int n = int.Parse(entrada);
        Console.WriteLine($"100 / {n} = {100 / n}");
    }
    catch (FormatException)
    {
        Console.WriteLine($"'{entrada}' não é número.");
    }
    catch (Exception e) when (e is DivideByZeroException or OverflowException)
    {
        Console.WriteLine($"'{entrada}': {e.GetType().Name}");
    }
    finally
    {
        Console.WriteLine("  (fim da tentativa)");
    }
}
$ dotnet run trycatch.cs
100 / 10 = 10
  (fim da tentativa)
'zero' não é número.
  (fim da tentativa)
'0': DivideByZeroException
  (fim da tentativa)
'99999999999': OverflowException
  (fim da tentativa)
FormaO que fazAula
try { … }o código que pode lançartry e catch
catch (FormatException) { … }pega esse tipo (e as filhas); sem variável, quando não precisa da mensagem
catch (IOException e) { … e.Message … }pega e dá um nome, para ler a mensagem
catch (Exception e) when (condição) C# 6só pega se a condição for verdadeira; senão, a exceção continua subindo
finally { … }roda sempre: deu certo, deu exceção tratada, ou não tratadafinally
using var recurso = …;um finally automático que fecha o recursousing e IDisposable

Lançar: throw

FormaQuando usarAula
throw new ArgumentException("…", nameof(param));o argumento recebido não servethrow
throw new InvalidOperationException("…");o objeto não está num estado que permite isso (sacar de uma conta encerrada)
ArgumentNullException.ThrowIfNull(x);o jeito curto de recusar null .NET 6
throw; (dentro do catch)relança a mesma exceção, mantendo o rastro original. Não escreva throw e;, que apaga o rastro.
throw new MinhaException("…", e);embrulha a original como InnerException (como no Carregar acima)
x ?? throw new …throw como expressão C# 7: Nome = nome ?? throw new ArgumentNullException(nameof(nome));Operadores ??

Evitar em vez de tratar

Exceção é para o que é excepcional. Para o que acontece sempre (o usuário digita errado, a chave não existe), há métodos que perguntam antes, sem exceção nenhuma:

Em vez de…Que lança…Use…
int.Parse(texto)FormatException, OverflowExceptionint.TryParse(texto, out int n)
d[chave]KeyNotFoundExceptiond.TryGetValue(chave, out var v)
lista.First(…)InvalidOperationExceptionlista.FirstOrDefault(…) e conferir o null
fila.Dequeue()InvalidOperationExceptionfila.TryDequeue(out var x)
(int)objetoInvalidCastExceptionif (objeto is int n)
File.ReadAllText(caminho) sem saber se existeFileNotFoundExceptionif (File.Exists(caminho)) (e ainda um try, porque o arquivo pode sumir entre as duas linhas)
texto.Length com texto anulávelNullReferenceExceptiontexto?.Length ?? 0

Experimente

Missão: a entrada já vem preenchida com vinte, e o programa cai com FormatException (código de saída 134). Troque o int.Parse por int.TryParse para ele mostrar Idade inválida. e terminar normalmente.

Console.Write("Sua idade: ");
string texto = Console.ReadLine() ?? "";
int idade = int.Parse(texto);
Console.WriteLine($"Ano que vem você terá {idade + 1}.");
Console.Write("Sua idade: ");
string texto = Console.ReadLine() ?? "";
if (int.TryParse(texto, out int idade))
{
    Console.WriteLine($"Ano que vem você terá {idade + 1}.");
}
else
{
    Console.WriteLine("Idade inválida.");
}

Experimente: descomente uma linha por vez e rode. Leia o tipo e a mensagem de cada exceção, depois conserte a linha.

int[] notas = [7, 9, 5];
string? apelido = null;
Dictionary<string, int> idades = new() { ["Ana"] = 29 };

// Console.WriteLine(notas[3]);
// Console.WriteLine(apelido!.Length);
// Console.WriteLine(idades["Bruno"]);
// Console.WriteLine(int.Parse("dez"));
// Console.WriteLine(new List<int>().First());
Console.WriteLine("Fim do programa.");

Resumo

  • Uma exceção não tratada mostra Unhandled exception., o tipo, a mensagem (em inglês) e o rastro, e o programa sai com o código 134.
  • Leia o rastro de cima para baixo até o primeiro arquivo seu: ali está a linha culpada.
  • As campeãs: NullReferenceException, IndexOutOfRangeException/ArgumentOutOfRangeException, FormatException, KeyNotFoundException, InvalidOperationException e FileNotFoundException.
  • Prefira evitar: TryParse, TryGetValue, FirstOrDefault, is, ?..
  • Para tratar: try/catch do mais específico para o mais geral, when para filtrar, finally ou using para liberar recursos, throw; para relançar.
  • Stack overflow não se trata: conserte a recursão.