* Add .NET reference files for temporal-developer skill Created 11 .NET reference files covering: dotnet.md (overview/quick start), patterns.md, determinism.md, determinism-protection.md, error-handling.md, testing.md, versioning.md, observability.md, data-handling.md, gotchas.md, and advanced-features.md. Follows Python/TypeScript patterns with .NET-specific content for Task determinism, CancellationToken, dependency injection, etc. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix .NET alignment issues from self-review - dotnet.md: Reduce Determinism Rules section to brief cross-reference (was duplicating determinism.md content) - patterns.md: Add ParentClosePolicy to Child Workflows example - gotchas.md: Add missing "Heartbeat Timeout Too Short" subsection - versioning.md: Add missing Key Concepts, Deployment Strategies, Query Filters, PINNED/AUTO_UPGRADE guidance, CLI examples - advanced-features.md: Add worker-level heading for exception types Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix .NET correctness issues from verification pass - patterns.md: Fix cancellation pattern to use official TemporalException.IsCanceledException(e) with detached CancellationTokenSource - advanced-features.md: Fix DI hosting example to use official AddHostedTemporalWorker(clientTargetHost:, clientNamespace:, taskQueue:) pattern Verified against official SDK README, API docs, and temporal-docs. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Update supported language references to include .NET - SKILL.md: Add "Temporal .NET" and "Temporal C#" trigger phrases, update overview to mention .NET, add .NET entry in getting started - core/determinism.md: Add .NET entry in SDK Protection Mechanisms Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Edits to advanced features * edits to determinism protection, and move the .editorconfig section * missed one * edit determinism.md * edit error-handling.md * edit gotchas.md * edit patterns.md * edit versioning.md * edit observability.md * fix metrics * self-review round 1 * minor correctness fixed * Update references/dotnet/patterns.md Co-authored-by: Justin Anderson <44687433+jmaeagle99@users.noreply.github.com> * address comments, clarify reference to earlier code snippet * clarify that operations are forbidden IN WORKFLOWS * cleanup workflow cancellation handling example * add task token retrieval comment * update .net requirements * Fix propagation of workflow cancellation --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Justin Anderson <44687433+jmaeagle99@users.noreply.github.com>
4.5 KiB
.NET SDK Error Handling
Overview
The .NET SDK uses ApplicationFailureException for application-specific errors and provides comprehensive retry policy configuration. Generally, the following information about errors and retryability applies across activities, child workflows and Nexus operations.
Application Failures
using Temporalio.Activities;
using Temporalio.Exceptions;
[Activity]
public async Task ValidateOrderAsync(Order order)
{
if (!order.IsValid())
{
throw new ApplicationFailureException(
"Invalid order",
errorType: "ValidationError");
}
}
Non-Retryable Errors
using Temporalio.Activities;
using Temporalio.Exceptions;
[Activity]
public async Task<string> ChargeCardAsync(ChargeCardInput input)
{
if (!IsValidCard(input.CardNumber))
{
throw new ApplicationFailureException(
"Permanent failure - invalid credit card",
errorType: "PaymentError",
nonRetryable: true); // Will not retry activity
}
return await ProcessPaymentAsync(input.CardNumber, input.Amount);
}
Handling Activity Errors in Workflows
using Temporalio.Workflows;
using Temporalio.Exceptions;
[Workflow]
public class MyWorkflow
{
[WorkflowRun]
public async Task<string> RunAsync()
{
try
{
return await Workflow.ExecuteActivityAsync(
(MyActivities a) => a.RiskyActivityAsync(),
new() { StartToCloseTimeout = TimeSpan.FromMinutes(5) });
}
catch (ActivityFailureException ex) when (!TemporalException.IsCanceledException(ex))
{
Workflow.Logger.LogError(ex, "Activity failed");
throw new ApplicationFailureException(
"Workflow failed due to activity error");
}
}
}
Retry Configuration
using Temporalio.Common;
using Temporalio.Workflows;
[Workflow]
public class MyWorkflow
{
[WorkflowRun]
public async Task<string> RunAsync()
{
return await Workflow.ExecuteActivityAsync(
(MyActivities a) => a.MyActivityAsync(),
new()
{
StartToCloseTimeout = TimeSpan.FromMinutes(10),
RetryPolicy = new()
{
MaximumInterval = TimeSpan.FromMinutes(1),
MaximumAttempts = 5,
NonRetryableErrorTypes = new[] { "ValidationError", "PaymentError" },
},
});
}
}
Only set options such as MaximumInterval, MaximumAttempts etc. if you have a domain-specific reason to. If not, prefer to leave them at their defaults.
Timeout Configuration
[Workflow]
public class MyWorkflow
{
[WorkflowRun]
public async Task<string> RunAsync()
{
return await Workflow.ExecuteActivityAsync(
(MyActivities a) => a.MyActivityAsync(),
new()
{
StartToCloseTimeout = TimeSpan.FromMinutes(5), // Single attempt
ScheduleToCloseTimeout = TimeSpan.FromMinutes(30), // Including retries
HeartbeatTimeout = TimeSpan.FromMinutes(2), // Between heartbeats
});
}
}
Workflow Failure
Critical .NET behavior: Only ApplicationFailureException will fail a workflow. All other exceptions (including standard .NET exceptions like NullReferenceException, KeyNotFoundException, etc.) will retry the workflow task indefinitely. This is by design — those are treated as bugs to be fixed with a code deployment, not reasons for the workflow to fail.
[Workflow]
public class MyWorkflow
{
[WorkflowRun]
public async Task<string> RunAsync()
{
if (someCondition)
{
throw new ApplicationFailureException(
"Cannot process order",
errorType: "BusinessError");
}
return "success";
}
}
Note: Do not use nonRetryable: with ApplicationFailureException inside a workflow (as opposed to an activity).
Best Practices
- Use specific error types for different failure modes
- Mark permanent failures as non-retryable in activities
- Configure appropriate retry policies
- Log errors before re-raising
- Use
ActivityFailureExceptionto catch activity failures in workflows - Design code to be idempotent for safe retries (see more at
references/core/patterns.md) - Only throw
ApplicationFailureExceptionfrom workflows to fail them — other exceptions will retry the workflow task