Tencent/WeKnora · error
encode sandbox binding: %w
Error message
encode sandbox binding: %w
What it means
Returned by RedisSessionSandboxBindingStore.Create when json.Marshal of the SessionSandboxBinding fails before the SET NX is issued. Marshal of a plain struct rarely fails, so this almost always indicates the binding contains a value JSON cannot represent (e.g. a channel, func, or cyclic value added to the struct) or a custom MarshalJSON that errors. Wrapped as 'encode sandbox binding: %w'.
Source
Thrown at internal/sandbox/session_binding_redis.go:147
if err := binding.Validate(key); err != nil {
return nil, fmt.Errorf("validate sandbox binding: %w", err)
}
return &binding, nil
}
// Create stores a validated current-schema binding with SET NX and no
// expiration.
func (s *RedisSessionSandboxBindingStore) Create(
ctx context.Context,
key SessionSandboxKey,
binding SessionSandboxBinding,
) (bool, error) {
if err := binding.Validate(key); err != nil {
return false, err
}
raw, err := json.Marshal(binding)
if err != nil {
return false, fmt.Errorf("encode sandbox binding: %w", err)
}
created, err := s.client.SetNX(ctx, s.bindingKey(key), raw, 0).Result()
if err != nil {
return false, fmt.Errorf("create sandbox binding: %w", err)
}
return created, nil
}
// DeleteIfMatch atomically deletes only the expected provider and sandbox ID.
func (s *RedisSessionSandboxBindingStore) DeleteIfMatch(
ctx context.Context,
key SessionSandboxKey,
provider RemoteProvider,
sandboxID string,
) (bool, error) {
if err := validateBindingMatch(key, provider, sandboxID); err != nil {
return false, err
}View on GitHub (pinned to 988cbb0330)
Solutions
- Inspect the wrapped %w error — it names the unsupported type; remove or replace the non-serializable field on SessionSandboxBinding
- Add a field with json:"-" if it is runtime-only and must not be persisted
- Ensure any custom MarshalJSON handles zero/nil values gracefully
- Unit-test Create with a fully populated binding to catch serialization regressions before deployment
Example fix
// before
type SessionSandboxBinding struct {
Provider RemoteProvider
SandboxID string
Runtime *SandboxRuntime // contains a channel: json.Marshal fails
}
// after
type SessionSandboxBinding struct {
Provider RemoteProvider
SandboxID string
Runtime *SandboxRuntime `json:"-"` // runtime-only, not persisted
} Defensive patterns
Strategy: validation
Validate before calling
// fail fast in tests/CI when SessionSandboxBinding gains non-serializable fields
func TestBindingIsJSONEncodable(t *testing.T) {
b := sandbox.SessionSandboxBinding{Provider: provider, SandboxID: "sbx-123"}
if _, err := json.Marshal(b); err != nil {
t.Fatalf("binding not encodable: %v", err)
}
} Try / catch
created, err := store.Create(ctx, key, binding)
if err != nil && strings.Contains(err.Error(), "encode sandbox binding") {
return fmt.Errorf("programming error: binding type not serializable: %w", err)
} Prevention
- Keep SessionSandboxBinding limited to JSON-native field types (strings, *time.Time)
- Mark runtime-only fields with json:"-"
- Add a round-trip Marshal/Unmarshal unit test for the binding struct
- Do not embed unexported types or interfaces holding channels/funcs in the struct
When it happens
Trigger: Calling Create(ctx, key, binding) where the binding (or a nested field added to SessionSandboxBinding) contains an unsupported type — channels, funcs, cycles — or implements MarshalJSON returning an error.
Common situations: A developer extended SessionSandboxBinding with a non-serializable field (e.g. *time.Ticker, sync primitives, interface holding a channel); a custom MarshalJSON implementation panics/errors on zero values; struct embedding pulled in unserializable fields.
Related errors
- encode stale sandbox binding: %w
- WEKNORA_REDIS_NAMESPACE must not contain braces
- WEKNORA_REDIS_NAMESPACE must not contain control characters
- failed to set FAQ metadata: %w
- marshal credentials: %w
AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02).
Data as JSON: /api/errors/59d11951e0c94aba.
Report an issue: GitHub.