Skip to main content
2026-06-30
Go SDK v1.0.0

Restate Go SDK 1.0

We’re pleased to announce the release of Restate Go SDK 1.0 🎉

Migration guide

If you’re upgrading from 0.x, follow this migration guide, or point your coding agent at it: MIGRATION.md.

Changes to the SDK modules

The SDK is now split into several independently versioned Go modules. In particular:
  • SDK remains github.com/restatedev/sdk-go
  • Testing now needs to be pulled separately using go get github.com/restatedev/sdk-go/[email protected], to avoid dependency pollution
We’re committing to 1.x semver guarantees for SDK and testing module and submodules, except internal and x submodules.We keep as experimental modules:

Revamped error interfaces

We’ve overhauled the error interfaces used in the SDK APIs to be more explicit and to make them easier to use and evolve:
  • TerminalError completes the invocation (or a Run) with a failure, with optional status code and metadata. You can build terminal errors using restate.ToTerminalError or restate.TerminalErrorf.
  • RetryableError is a non-terminal failure that gets retried (a status code). We will evolve this type in the future to support customizing the retry behavior.
BREAKING: Because TerminalError is now a type, the old TerminalError(...) constructor was renamed to ToTerminalError:
Context operations such as Get, Run, Call and Wait now return a TerminalError directly instead of a generic error. This makes the SDK’s error-handling model explicit right in the signatures: return a TerminalError for failures that are recorded in the Restate journal, and panic on any other error to let Restate retry.

Randomness uses math/rand/v2

Rand(ctx) now returns a standard-library *math/rand/v2.Rand instead of a custom interface, so the full stdlib API is available. It’s still seeded deterministically (same sequence on every replay) and must not be used inside Run. UUIDs moved to their own helper:
id := restate.Rand(ctx).UUID().String()   // 0.x
id := restate.UUID(ctx).String()          // 1.0

// the whole math/rand/v2 surface now works (deterministic, replay-safe):
r := restate.Rand(ctx)
roll := r.IntN(6) + 1

Solidified options

We’ve cleaned up the option vocabulary across the SDK so it’s consistent and easier to evolve:
  • Retry policy options are now a single shared vocabulary: the same builders work for Run/RunAsync/RunVoid and for the invocation retry policy (WithInvocationRetryPolicy), with consistent names and types.
  • Codecs are simplified: a single encoding.Codec is used everywhere (handlers, services, ingress, value operations), set with WithCodec, and input/output codecs can now be configured independently via WithInputCodec/WithOutputCodec.
See MIGRATION.md for the exact renames.

(Preview) Scope and limit key to enable flow control

This release adds the SDK API for flow control, the new Restate 1.7 feature to cap how many invocations run concurrently within a scope, with optional hierarchical limit keys. Use it to bound cost (especially for AI agents, where every concurrent invocation is real model/API spend), shield downstream systems from bursts, and share capacity fairly across tenants.To use scope and limit keys in service to service communication:
// Route every call made through this client into a named scope.
out, err := restate.Service[Output](ctx, "MyService", "Process",
    restate.WithScope("tenant-123")).
    Request(input)

// Add a hierarchical limit key for finer-grained concurrency limits.
out, err := restate.Service[Output](ctx, "MyService", "Process",
    restate.WithScope("tenant-123")).
    Request(input, restate.WithLimitKey("api-key/user42"))
WithScope works on Service, Object and Workflow clients (and their …Send variants); WithLimitKey works on both in-context calls and ingress requests/sends.
NOTE: This API is in preview and is not enabled by default. To use it in restate-server 1.7, enable the flow control and protocol v7 experimental features via RESTATE_EXPERIMENTAL_ENABLE_PROTOCOL_V7=true and RESTATE_EXPERIMENTAL_ENABLE_VQUEUES=true (new clusters only). See https://docs.restate.dev/services/flow-control for details.

New Signals API

Signals are a first-class way for one invocation to communicate with another: an invocation waits on a named signal, and any other handler resolves or rejects it by referencing the target invocation’s ID.
// Inside the waiting invocation — block until the signal arrives:
approved, err := restate.Signal[bool](ctx, "approved").Result()

// A SignalFuture can also be combined with others via Context.Select,
// exactly like awakeables, calls and timers.
// Inside another handler — resolve or reject by target invocation ID:
restate.ResolveSignal(ctx, targetInvocationID, "approved", true)
restate.RejectSignal(ctx, targetInvocationID, "approved", restate.TerminalErrorf("denied"))

Noteworthy bumps

  • Minimum Go version: 1.24 → 1.25.
  • testcontainers-go removed from the core module. It now lives only in the testing module (bumped to v0.43.0 there), so consumers of the SDK no longer inherit Docker and its dependency tree.
  • go.opentelemetry.io/otel v1.38.0 → v1.44.0
  • google.golang.org/protobuf v1.36.10 → v1.36.11
  • github.com/invopop/jsonschema v0.13.0 → v0.14.0

What’s Changed

New Contributors

View on GitHub
2026-06-30
Go SDK testing/v1.0.0
2026-04-02
Go SDK v0.24.0

What’s Changed

New Contributors

View on GitHub
2026-01-05
Go SDK v0.23.0

What’s Changed

Better integration with go’s Context

We have improved the interoperability with go’s Context, you can now wrap any context inside restate.Context using restate.WrapContext:
func TracingExample(ctx restate.Context) error {
	// Create a span
	traceCtx, exampleSpan := otelTracer.Start(ctx, "Example")
	// Wrap the traceCtx created by otel in the restate context.
	ctx = restate.WrapContext(ctx, traceCtx)
	defer exampleSpan.End()

	return restate.RunVoid(ctx, func(ctx restate.RunContext) error {
		// RunContext will propagate correctly the wrapped otel context too. 
		_, innerExampleSpan := otelTracer.Start(ctx, "InnerExample")
		defer innerExampleSpan.End()

		// Do something
		return nil
	})
}
You can also propagate values manually in the usual way using restate.WithValue:
// Wrap using restate.WithValue
ctx = restate.WithValue(ctx, "tenant", tenant)

restate.Run(ctx, func(ctx restate.RunContext) (string, error) {
  // Extract using context APIs
  tenant := ctx.Value("tenant").(string)
})

Other changes

View on GitHub
2025-12-16
Go SDK v0.22.1

What’s Changed

View on GitHub
2025-11-20
Go SDK v0.22.0

New features:

  • Added restate.RunVoid as shortcut method to execute runs that return no value:
err = restate.RunVoid(ctx, func(stepCtx restate.RunContext) error {
  return DoStuff()  
})
Thanks @chronark for the contribution!
  • Added test environment using testcontainers:
import (
	"testing"

	restatetest "github.com/restatedev/sdk-go/testing"
	"github.com/restatedev/sdk-go/ingress"
)

func TestWithTestcontainers(t *testing.T) {
	// Setup test environment
	tEnv := restatetest.Start(t, restate.Reflect(Greeter{}))
	// Client to do requests
	client := tEnv.Ingress()

	// Do request to greeter, and assert response
	out, err := ingress.Service[string, string](client, "Greeter", "Greet").Request(t.Context(), "Francesco")
	require.NoError(t, err)
	require.Equal(t, "You said hi to Francesco!", out)
}

New Contributors

View on GitHub
2025-10-29
Go SDK v0.21.1

What’s Changed

View on GitHub
2025-10-20
Go SDK v0.21.0

New features

  • protoc-restate-go now generates ingress clients
counterClient := helloworld.NewCounterIngressClient(client, "my-counter-key")

addRes, err := counterClient.Add().Request(ctx, &helloworld.AddRequest{Delta: 1}, restate.WithDelay(10*time.Second))
  • New API to concurrently wait Restate Futures:
    • WaitFirst to wait the first of the given futures to complete
fut1 := restate.Service[string](ctx, "service1", "method1").RequestFuture(input)
fut2 := restate.After(ctx, 5 * time.Second)

firstCompleted, err := restate.WaitFirst(ctx, fut1, fut2, fut3)
if err != nil {
	return "", err
}
// Handle the first completed future
switch firstCompleted {
case fut1:
	return fut1.Response()
case fut2:
	return "", fmt.Errorf("timeout")
}
  • Wait to create an iterator (usable in for range) to await futures as soon as they’re completed
fut1 := restate.Service[string](ctx, "service1", "method1").RequestFuture(input)
fut2 := restate.Service[string](ctx, "service2", "method2").RequestFuture(input)
fut3 := restate.Service[string](ctx, "service3", "method3").RequestFuture(input)

results := []string{}
for fut, err := range restate.Wait(ctx, fut1, fut2, fut3) {
	if err != nil {
		return nil, err
	}
	result, err := fut.(restate.ResponseFuture[string]).Response()
	if err != nil {
		return nil, err
	}
	results = append(results, result)
}
  • WaitIter, like Wait, but with a “traditional iterator” API
  • All the new selector API now correctly support Restate cancellation feature.

Deprecations

  • restate.Rand(ctx).UUID() -> restate.UUID(ctx)
  • restate.Rand(ctx).Source() -> restate.RandSource(ctx)
  • In subsequent releases, restate.Rand(ctx) will return math/rand/v2 Rand object instead of the internal interface.
  • Few changes to the Ingress API, including renaming the Attach* API:
    • AttachInvocation -> InvocationById
    • AttachService -> ServiceInvocationByIdempotencyKey
    • AttachObject -> ObjectInvocationByIdempotencyKey
    • AttachWorkflow -> WorkflowHandle
  • Select API is deprecated, replace it with WaitFirst, Wait or WaitIter

Breaking changes

  • Ingress API
    • Send return type changed, now it returns SendResponse/SimpleSendResponse and splits result type to error type.
    • Return type of Service/Object/Workflow now doesn’t implement anymore SendRequester, but was replaced with an ad-hoc interface to carry on the generic information

Fixes

  • Ingress API deserialized incorrectly send and attach responses, when used in combination with a Codec different from the default one.

Full changelog

View on GitHub
2025-10-07
Go SDK v0.20.2

What’s Changed

View on GitHub
2025-10-07
Go SDK v0.20.1

What’s Changed

View on GitHub
2025-09-17
Go SDK v0.20.0

Invocation retry policy

When used with Restate 1.5, you can now configure the invocation retry policy from the SDK directly. See https://github.com/restatedev/restate/releases/tag/v1.5.0 for more details on the new invocation retry policy configuration.

More configuration

Allow to configure per-handler options in the reflection API through ConfigureHandler.

What’s Changed

View on GitHub
2025-08-18
Go SDK v0.19.0

What’s Changed

New Contributors

View on GitHub
2025-07-21
Go SDK v0.18.1
Fix goroutine leak in read path, see https://github.com/restatedev/sdk-go/pull/86 for more details.View on GitHub
2025-07-02
Go SDK v0.18.0
  • Added new options for services and handlers WithAbortTimeout, WithEnableLazyState, WithIdempotencyRetention, WithInactivityTimeout, WithIngressPrivate, WithJournalRetention and WithWorkflowRetention. Please note, these will work only from Restate 1.4 onward. Check the in-code documentation for more details.
  • Introduce new ingress client, thanks @strobus for the contribution!
  • Several improvements to error messages
  • Fix bug with protojson and schema generation

Changelog

View on GitHub
2025-06-17
Go SDK v0.17.1

What’s changed

https://github.com/restatedev/sdk-go/pull/73 - fix a bug related to HTTP1.1 serversView on GitHub
2025-04-29
Go SDK v0.17.0

What’s Changed

View on GitHub
2025-04-09
Go SDK v0.16.0
We’re pleased to announce the release of Golang SDK 0.16, in combination with Restate 1.3. Check out the announcement blog post for more details about Restate 1.3 and the new SDK features: https://restate.dev/blog/announcing-restate-1.3/This SDK introduces the following new APIs:Golang SDK 0.16 can be used in combination with Restate 1.3 onward.

Full changelog

New Contributors

View on GitHub
2025-02-12
Go SDK v0.15.0

What’s Changed

View on GitHub
2025-01-20
Go SDK v0.14.0

What’s Changed

View on GitHub
2024-12-11
Go SDK v0.13.2
Fix go.mod to allow go install to workView on GitHub
2024-11-26
Go SDK v0.13.1

What’s Changed

New Contributors

View on GitHub
2024-11-08
Go SDK v0.12.0

What’s Changed

View on GitHub
2024-08-23
Go SDK v0.11.0
This PR breaks essentially the entire API, as methods have been moved from Context.Run et all to restate.Run(ctx) et all, which is much more pleasant.Serialisation issues will now always panic - this allows us to take error return values out of various api calls that don’t need them.The semantics of .Get() have changed - the zero value is now always returned if the key isn’t found. You can check explicitly for this case by providing a pointer type eg *string. Get will now only return errors in the case of cancellation, which will also only happen if eager state is disabled, which is not the default. As such, errors from gets are now very unlikely and you can just return them without another thought.It is also notable that you can now provide many more method signatures to .Reflect().

What’s Changed

View on GitHub
2024-08-16
Go SDK v0.10.0
There are several breaking changes in this release:
  1. NewServiceRouter et al -> NewService
  2. restate.Service and restate.Object -> restate.Reflect
  3. Sleep now returns an error, which is returned in the case of invocation cancellation
There is also a significant new feature; code generation. For other changes, see below

What’s Changed

New Contributors

View on GitHub
2024-07-19
Go SDK v0.9.1

What’s Changed

View on GitHub
2024-07-17
Go SDK v0.9.0
With v0.9.0, this repository is compatible with Restate v1, and is much closer to feature complete with other repos. Correctness has been validated using the Restate verification tests in restatedev/e2e. A 1.0 release will come when we have API stability, which will not come until we have some feedback from users.

What’s Changed

New Contributors

View on GitHub