Tencent/WeKnora · error
sandbox: backend must be constructed via…
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).
Solutions
- Use NewManagerFromType("cube"|"e2b"|"docker", dockerImage) which routes remote types to NewSessionBoundManager.
- Call NewSessionBoundManager directly with the required session binding configuration.
- If you only need the default manager, set config.Type to a stateless-supported type (e.g. local/disabled).
- 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
- Route remote backend types exclusively through NewManagerFromType
- Document that DefaultManager is for stateless backends only
- Add unit tests asserting constructor choice per sandbox type
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
- resolve remote sandbox for session
- sandbox: no live sandbox for session
- check owning session
- consume sandbox turn rebuild
- CPU limit cannot be negative
AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02).
Data as JSON: /api/errors/b53b79efcb139cee.
Report an issue: GitHub.
Appendix: 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, ErrSandboxDisabledView on GitHub (pinned to 988cbb0330)