{"record":{"id":"5644d36bb0832469","repo":"Tencent/WeKnora","slug":"sandbox-binding-requires-tenant-and-session","errorCode":null,"errorMessage":"sandbox binding requires tenant and session","messagePattern":"sandbox binding requires tenant and session","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/sandbox/session_binding.go","lineNumber":25,"sourceCode":"\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\t\"unicode\"\n)\n\n// SessionSandboxBindingVersion is the current persisted binding schema.\nconst SessionSandboxBindingVersion = 1\n\n// SessionSandboxKey identifies one tenant-scoped persistent sandbox.\ntype SessionSandboxKey struct {\n\tTenantID  uint64\n\tSessionID string\n}\n\n// Validate rejects keys that cannot identify a tenant session.\nfunc (k SessionSandboxKey) Validate() error {\n\tif k.TenantID == 0 || strings.TrimSpace(k.SessionID) == \"\" {\n\t\treturn errors.New(\"sandbox binding requires tenant and session\")\n\t}\n\tif strings.ContainsAny(k.SessionID, \"{}\") {\n\t\treturn errors.New(\"sandbox binding session must not contain braces\")\n\t}\n\tfor _, r := range k.SessionID {\n\t\tif unicode.IsControl(r) {\n\t\t\treturn errors.New(\"sandbox binding session must not contain control characters\")\n\t\t}\n\t}\n\treturn nil\n}\n\n// SessionSandboxBinding records the remote sandbox assigned to a session.\ntype SessionSandboxBinding struct {\n\tVersion    int            `json:\"version\"`\n\tProvider   RemoteProvider `json:\"provider,omitempty\"`\n\tTenantID   uint64         `json:\"tenant_id\"`\n\tSessionID  string         `json:\"session_id\"`","sourceCodeStart":7,"sourceCodeEnd":43,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/sandbox/session_binding.go#L7-L43","documentation":"SessionSandboxKey.Validate requires a key to identify a tenant session: TenantID must be non-zero and SessionID must be non-blank. This error means one or both are missing, so the sandbox cannot be bound, looked up, or used for a turn (Get, WithLifecycleLock, BeginTurn, EndTurn all validate first).","triggerScenarios":"Calling Get/BeginTurn/EndTurn/WithLifecycleLock with a SessionSandboxKey where TenantID == 0, or SessionID is empty/whitespace only.","commonSituations":"Building the key before the auth/session context is resolved so TenantID is still the zero value; a request missing the session identifier; an unset struct field after a refactor.","solutions":["Populate TenantID with the authenticated tenant's ID before building the key.","Trim and check the session ID at the API boundary and reject requests lacking it.","Construct SessionSandboxKey only after tenant and session resolution succeeds, then call Validate early to fail fast."],"exampleFix":"// before\nkey := sandbox.SessionSandboxKey{SessionID: sessionID} // TenantID missing\nerr := key.Validate() // \"sandbox binding requires tenant and session\"\n// after\nkey := sandbox.SessionSandboxKey{TenantID: tenantID, SessionID: strings.TrimSpace(sessionID)}\nerr := key.Validate()","handlingStrategy":"validation","validationCode":"func keyReady(tenantID int64, sessionID string) error {\n    if tenantID == 0 { return errors.New(\"tenant id required\") }\n    if strings.TrimSpace(sessionID) == \"\" { return errors.New(\"session id required\") }\n    return nil\n}","typeGuard":"func validSessionKey(k sandbox.SessionSandboxKey) bool {\n    return k.TenantID != 0 && strings.TrimSpace(k.SessionID) != \"\"\n}","tryCatchPattern":"if err := key.Validate(); err != nil {\n    return fmt.Errorf(\"bad session sandbox key (tenant=%d session=%q): %w\", key.TenantID, key.SessionID, err)\n}","preventionTips":["Resolve tenant and session from auth context before constructing the key.","Use a constructor that enforces non-zero tenant and non-blank session ID.","Add Validate() calls at handler entry points."],"tags":["go","sandbox","multi-tenancy","validation"],"backgroundTag":"missing-required-argument","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}