Tencent/WeKnora · error

sandbox binding version must be %d, got %d

Error message

sandbox binding version must be %d, got %d

What it means

SessionSandboxBinding.Validate enforces that a persisted binding was written with the current SessionSandboxBindingVersion schema. If b.Version differs (older rows from a previous release, or corrupted data) it returns this formatted mismatch error naming the expected and actual versions. Callers like BeginTurn/EndTurn/Get depend on it to reject stale bindings early.

Source

Thrown at internal/sandbox/session_binding.go:70

	//
	// Bindings written before this field existed carry an empty value. They are
	// deliberately still valid — refusing them would break every live session
	// on upgrade — and simply match no config until they are rebuilt.
	ConfigID string `json:"config_id,omitempty"`

	// StaleAt marks a binding whose sandbox boots an image the config has
	// since replaced. The sandbox keeps serving until the session's next
	// resolve, which destroys and recreates it; see InvalidateByConfig.
	StaleAt *time.Time `json:"stale_at,omitempty"`
}

// Validate checks a binding against the current schema and authoritative key.
func (b SessionSandboxBinding) Validate(key SessionSandboxKey) error {
	if err := key.Validate(); err != nil {
		return err
	}
	if b.Version != SessionSandboxBindingVersion {
		return fmt.Errorf(
			"sandbox binding version must be %d, got %d",
			SessionSandboxBindingVersion,
			b.Version,
		)
	}
	if !isRemoteProvider(b.Provider) {
		return fmt.Errorf("unsupported sandbox binding provider %q", b.Provider)
	}
	if b.TenantID != key.TenantID || b.SessionID != key.SessionID {
		return errors.New("sandbox binding identity does not match its key")
	}
	if strings.TrimSpace(b.SandboxID) == "" {
		return errors.New("sandbox binding requires sandbox ID")
	}
	if strings.TrimSpace(b.TemplateID) == "" {
		return errors.New("sandbox binding requires template ID")
	}
	if b.CreatedAt.IsZero() {

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Run the library's binding data migration or rewrite stored bindings to the current SessionSandboxBindingVersion
  2. Delete/invalidate stale bindings so they are recreated on the next BeginTurn
  3. Ensure all service instances run the same library version during rollouts
  4. Set Version: SessionSandboxBindingVersion explicitly when constructing bindings in tests

Example fix

// before
b := sandbox.SessionSandboxBinding{Provider: "docker", SandboxID: "sbx1"}
// after
b := sandbox.SessionSandboxBinding{
    Provider: "docker", SandboxID: "sbx1",
    Version: sandbox.SessionSandboxBindingVersion,
}
Defensive patterns

Strategy: type-guard

Validate before calling

if b.Version != sandbox.SessionSandboxBindingVersion {
    return migrateOrRecreateBinding(b)
}

Type guard

func bindingCurrent(b sandbox.SessionSandboxBinding) bool {
    return b.Version == sandbox.SessionSandboxBindingVersion
}

Try / catch

if err := b.Validate(key); err != nil {
    var verr *fmt.Errorf
    if errors.As(err, &verr) && strings.Contains(err.Error(), "version must be") {
        return recreateBinding(ctx, key) // drop stale-schema binding
    }
    return err
}

Prevention

When it happens

Trigger: Loading a binding from storage (via Get or during BeginTurn/EndTurn) that was written by an older library version with a different binding schema version, or a binding struct populated by hand with a wrong Version field.

Common situations: Rolling deploy where old-version bindings persist in Redis while the new binary validates them; hand-crafted test fixtures with Version left at zero value; data migrations not run.

Related errors


AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02). Data as JSON: /api/errors/5b7633504c2a102c. Report an issue: GitHub.