{"record":{"id":"fdfe57c291cd6294","repo":"Tencent/WeKnora","slug":"consume-sandbox-turn-rebuild-w","errorCode":null,"errorMessage":"consume sandbox turn rebuild: %w","messagePattern":"consume sandbox turn rebuild: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/sandbox/session_binding_redis.go","lineNumber":375,"sourceCode":"\trefs, _ := strconv.Atoi(values[\"refs\"])\n\tif refs <= 0 {\n\t\treturn false, false, nil\n\t}\n\treturn true, values[\"rebuild\"] == \"1\", nil\n}\n\n// ConsumeTurnRebuild spends the one rebuild allowed for the current turn.\nfunc (s *RedisSessionSandboxBindingStore) ConsumeTurnRebuild(\n\tctx context.Context,\n\tkey SessionSandboxKey,\n) error {\n\tif err := key.Validate(); err != nil {\n\t\treturn err\n\t}\n\tif err := consumeTurnRebuildScript.Run(\n\t\tctx, s.client, []string{s.turnKey(key)}, sessionTurnLeaseTTL.Milliseconds(),\n\t).Err(); err != nil {\n\t\treturn fmt.Errorf(\"consume sandbox turn rebuild: %w\", err)\n\t}\n\treturn nil\n}\n\nfunc (s *RedisSessionSandboxBindingStore) turnKey(key SessionSandboxKey) string {\n\treturn \"weknora:sandbox:session:{\" + s.hashTag(key) + \"}:turn\"\n}\n\nfunc (s *RedisSessionSandboxBindingStore) bindingKey(key SessionSandboxKey) string {\n\treturn \"weknora:sandbox:session:{\" + s.hashTag(key) + \"}:binding\"\n}\n\nfunc (s *RedisSessionSandboxBindingStore) lockKey(key SessionSandboxKey) string {\n\t// Keep the historical suffix used by the saved multi-node Cube\n\t// implementation so rolling upgrades serialize on the same lock.\n\treturn \"weknora:sandbox:session:{\" + s.hashTag(key) + \"}:create-lock\"\n}\n","sourceCodeStart":357,"sourceCodeEnd":393,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/sandbox/session_binding_redis.go#L357-L393","documentation":"ConsumeTurnRebuild runs a Lua script that atomically spends the single stale-sandbox rebuild allowed for the current turn in the Redis turn-lease hash. This error wraps any failure of that script execution (connection error, script error, NOSCRIPT, context cancellation). It is thrown because silently skipping consumption could let a later mid-turn install tear down a sandbox that in-flight work depends on.","triggerScenarios":"RedisSessionSandboxBindingStore.ConsumeTurnRebuild is called (from resolveLocked after stale handling) when Redis is down, the EVALSHA/EVAL call fails, the script returns an error, or ctx is cancelled during the round trip.","commonSituations":"Redis failover mid-request; Redis SCRIPT FLUSH causing transient NOSCRIPT (usually auto-retried by go-redis, but surfaces if retries are disabled); network blips; Redis OOM refusing writes.","solutions":["Verify Redis availability and connectivity; fix connection/auth config.","Unwrap the error chain: if it is a retryable Redis I/O error, retry the operation — consuming the rebuild is idempotent in effect per turn.","Check Redis memory (maxmemory) if OOM errors are wrapped, and raise limits or enable eviction policy appropriate for these keys.","Ensure all WeKnora nodes load the same script version (matching deployments) to avoid persistent script mismatches."],"exampleFix":"// before\nif err := store.ConsumeTurnRebuild(ctx, key); err != nil {\n    return err\n}\n// after\nif err := store.ConsumeTurnRebuild(ctx, key); err != nil {\n    if isRetryableRedisErr(err) { // e.g. io.EOF, net timeout, redis.Nil-free I/O errors\n        if retryErr := store.ConsumeTurnRebuild(ctx, key); retryErr != nil {\n            return fmt.Errorf(\"consume sandbox turn rebuild: %w\", retryErr)\n        }\n    } else {\n        return err\n    }\n}","handlingStrategy":"retry","validationCode":"if err := rdb.Ping(ctx).Err(); err != nil {\n    return fmt.Errorf(\"redis unavailable before ConsumeTurnRebuild: %w\", err)\n}","typeGuard":"func isRetryableRedisErr(err error) bool {\n    var rerr *redis.RedisError\n    return errors.As(err, &rerr) && !strings.Contains(err.Error(), \"NOSCRIPT\")\n}","tryCatchPattern":"if err := store.ConsumeTurnRebuild(ctx, key); err != nil {\n    if isRetryableRedisErr(err) {\n        err = store.ConsumeTurnRebuild(ctx, key)\n    }\n    if err != nil { return fmt.Errorf(\"consume rebuild: %w\", err) }\n}","preventionTips":["Deploy all nodes with matching script versions to avoid NOSCRIPT drift.","Keep Redis memory headroom so writes (rebuild flag consumption) never OOM.","Wrap turn-boundary work in a context with enough budget for Redis round trips.","Alert on Redis write failures during resolve paths."],"tags":["redis","lua-script","sandbox","turn-lease"],"backgroundTag":"redis-unreachable","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}