{"record":{"id":"1f62b931d0b4a731","repo":"temporalio/temporal","slug":"failed-to-access-valid-object-from-globalrngpool","errorCode":null,"errorMessage":"Failed to access valid object from globalRngPool","messagePattern":"Failed to access valid object from globalRngPool","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"common/fastrand/fastrand.go","lineNumber":30,"sourceCode":"\n// globalRngPool is a globally shared object for allowing lock-free reuse\n// of shared random number generators. In practice we would not expect this\n// pool to contain many more objects than the number of CPU cores running\n// the code.\nvar globalRngPool = sync.Pool{\n\tNew: func() any {\n\t\treturn rand.New(rand.NewSource(rand.Int63()))\n\t},\n}\n\n// getRng returns a thread-local [math/rand.Rand] object, largely a wrapper\n// for globalRngPool.Get(), with a typecast and assertion that everything is\n// as expected.\nfunc getRng() *rand.Rand {\n\trng, ok := globalRngPool.Get().(*rand.Rand)\n\tif !ok {\n\t\t// nolint:forbidigo\n\t\tpanic(\"Failed to access valid object from globalRngPool\") // This should never happen, since it would mean someone put an invalid object into the pool.\n\t}\n\n\treturn rng\n}\n\n// Rand is an object that behaves largely as-if it was a [math/rand.Rand],\n// with the key distinction that it is thread-safe and highly performant.\n//\n// Under the hood this uses a thread-safe pool of [math/rand.Rand] objects\n// which it will dynamically create and access for each call. As a result,\n// this does not support setting the seed, since the underlying objects\n// are ephemeral.\ntype Rand struct{}\n\n// ExpFloat64 implements [math/rand.Rand.ExpFloat64].\nfunc (r Rand) ExpFloat64() float64 {\n\trng := getRng()\n\tres := rng.ExpFloat64()","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/temporalio/temporal/blob/bde624efd13fbd3843654058db6d9c716166318b/common/fastrand/fastrand.go#L12-L48","documentation":"fastrand maintains a pool of *rand.Rand objects; getRng retrieves one and asserts the type. If the pooled object is not a *rand.Rand — which can only happen if something Put an invalid object into the pool — the code panics, since continuing would corrupt all randomness-based behavior.","triggerScenarios":"Essentially never from normal use: it requires code to Put a non-*rand.Rand value into globalRngPool. Practically triggered only by edits to the fastrand package itself or tests that tamper with the pool.","commonSituations":"A developer modifies the pool (adds a differently-typed object, wraps the pool, or changes the put-side type) and a subsequent Int/Float64/etc. call panics; nil pointer stored via a bad Put.","solutions":["Fix the code that Puts a non-*rand.Rand object into globalRngPool","Ensure the put path always stores the same concrete type obtained from the package's own factory","Add a test asserting pool round-trip type integrity"],"exampleFix":"// before\nglobalRngPool.Put(someOtherRng)\n// after\nglobalRngPool.Put(rng) // rng must be *rand.Rand from the package's constructor","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"rng, ok := v.(*rand.Rand)","tryCatchPattern":null,"preventionTips":["Typed round-trip tests for the pool","Never Put foreign types"],"tags":["go","panic","random","type-assertion","sync-pool"],"backgroundTag":"invalid-type-assertion","analyzedSha":"bde624efd13fbd3843654058db6d9c716166318b","analyzedAt":"2026-09-01T07:18:39.080Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}