Tencent/WeKnora · error

sandbox: %s backend must be constructed via NewSessionBoundM

Error message

sandbox: %s backend must be constructed via NewSessionBoundManager

What it means

DefaultManager.initializeSandbox refuses remote, session-scoped backends (Cube, E2B, Docker). These backends need per-session state that only SessionBoundManager owns via its authoritative session binding, so DefaultManager intentionally rejects them and points the caller at NewSessionBoundManager (which NewManagerFromType routes to automatically).

Source

Thrown at internal/sandbox/manager.go:56

	}

	return manager, nil
}

// initializeSandbox creates and configures the sandbox based on configuration
func (m *DefaultManager) initializeSandbox(ctx context.Context) error {
	switch m.config.Type {
	case SandboxTypeDisabled:
		m.sandbox = &disabledSandbox{}
		return nil

	case SandboxTypeCube, SandboxTypeE2B, SandboxTypeDocker:
		// Session-scoped remote backends are only reachable through
		// SessionBoundManager, which owns the authoritative binding.
		// DefaultManager exposes stateless semantics that cannot preserve
		// per-session state, so we refuse the construction and let
		// NewManagerFromType route the caller to NewSessionBoundManager.
		return fmt.Errorf(
			"sandbox: %s backend must be constructed via NewSessionBoundManager",
			m.config.Type,
		)

	default:
		return fmt.Errorf("unknown sandbox type: %s", m.config.Type)
	}
}

// Execute runs a script using the configured sandbox
// It performs security validation before execution to prevent prompt injection attacks
func (m *DefaultManager) Execute(ctx context.Context, config *ExecuteConfig) (*ExecuteResult, error) {
	m.mu.RLock()
	sandbox := m.sandbox
	m.mu.RUnlock()

	if sandbox == nil {
		return nil, ErrSandboxDisabled

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Use NewManagerFromType("cube"|"e2b"|"docker", dockerImage) which routes remote types to NewSessionBoundManager.
  2. Call NewSessionBoundManager directly with the required session binding configuration.
  3. If you only need the default manager, set config.Type to a stateless-supported type (e.g. local/disabled).
  4. Refactor call sites to pick the manager constructor based on backend type.

Example fix

// before
config := DefaultConfig(); config.Type = SandboxTypeE2B
mgr, err := NewManager(config)
// after
mgr, err := NewManagerFromType("e2b", "") // routes to NewSessionBoundManager
Defensive patterns

Strategy: validation

Validate before calling

switch cfg.Type {
case SandboxTypeCube, SandboxTypeE2B, SandboxTypeDocker:
    return errors.New("use NewManagerFromType / NewSessionBoundManager for this backend")
}

Type guard

func needsSessionBound(t SandboxType) bool {
    switch t {
    case SandboxTypeCube, SandboxTypeE2B, SandboxTypeDocker:
        return true
    }
    return false
}

Try / catch

mgr, err := NewManager(cfg)
if err != nil && strings.Contains(err.Error(), "NewSessionBoundManager") {
    mgr, err = NewManagerFromType(string(cfg.Type), "")
}

Prevention

When it happens

Trigger: Calling NewManager with a Config whose Type is SandboxTypeCube, SandboxTypeE2B, or SandboxTypeDocker; NewManager then fails during initializeSandbox.

Common situations: Choosing the wrong constructor: using NewManager instead of NewManagerFromType for remote sandbox types; older code written before session-bound managers existed; test code copying a config from a remote deployment into NewManager.

Related errors


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