{"record":{"id":"da8c21303c2f28b4","repo":"Tencent/WeKnora","slug":"sandbox-binding-session-must-not-contain-braces","errorCode":null,"errorMessage":"sandbox binding session must not contain braces","messagePattern":"sandbox binding session must not contain braces","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/sandbox/session_binding.go","lineNumber":28,"sourceCode":"\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\"`\n\tSandboxID  string         `json:\"sandbox_id\"`\n\tTemplateID string         `json:\"template_id\"`\n\tCreatedAt  time.Time      `json:\"created_at\"`","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/sandbox/session_binding.go#L10-L46","documentation":"SessionSandboxKey.Validate rejects session IDs containing '{' or '}'. Braces in the session ID would corrupt the Redis key/namespace templating used for sandbox bindings, so such keys are rejected outright.","triggerScenarios":"Passing a SessionSandboxKey whose SessionID contains '{' or '}' to Validate (directly or via Get, BeginTurn, EndTurn, WithLifecycleLock, validateBindingMatch), typically because the ID came from a URL template or JSON snippet.","commonSituations":"Client sends an ID copied with surrounding braces like \"{abc-123}\"; IDs generated from template strings that kept placeholder braces.","solutions":["Strip enclosing braces before building the key, e.g. strings.Trim(id, \"{}\").","Sanitize session IDs at the ingress point and reject brace-containing IDs with a 400.","Ensure internal ID generators emit UUIDs without braces."],"exampleFix":"// before\nkey := sandbox.SessionSandboxKey{TenantID: 7, SessionID: \"{abc-123}\"}\n// after\nkey := sandbox.SessionSandboxKey{TenantID: 7, SessionID: strings.Trim(sessionID, \"{}\")}","handlingStrategy":"validation","validationCode":"var sessionIDRe = regexp.MustCompile(`^[A-Za-z0-9._-]+$`)\nif !sessionIDRe.MatchString(sessionID) {\n    return errors.New(\"session id contains forbidden characters\")\n}","typeGuard":"func safeSessionID(id string) bool { return !strings.ContainsAny(id, \"{}\") && strings.TrimSpace(id) != \"\" }","tryCatchPattern":"if err := key.Validate(); err != nil {\n    if strings.Contains(err.Error(), \"braces\") {\n        key.SessionID = strings.Trim(key.SessionID, \"{}\")\n        return key.Validate()\n    }\n    return err\n}","preventionTips":["Sanitize IDs at ingress with a strict allowlist regex.","Trim braces when accepting UUIDs in brace form.","Generate session IDs as bare hex UUIDs."],"tags":["go","sandbox","validation","session-id"],"backgroundTag":"invalid-session-id","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}