{"record":{"id":"f4ff7c95253c7489","repo":"temporalio/temporal","slug":"parallelsuite-do-not-call-t-after-run-use-th","errorCode":null,"errorMessage":"parallelsuite: do not call T() after Run(); use the subtest callback's parameter instead","messagePattern":"parallelsuite: do not call T\\(\\) after Run\\(\\); use the subtest callback's parameter instead","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"common/testing/parallelsuite/suite.go","lineNumber":103,"sourceCode":"\tg.hasSubtests.Store(false)\n\ts.runParallel = parallel\n\ts.ctx = ctx\n\tif s.runParallel {\n\t\tt.Parallel() //nolint:testifylint // parallelsuite intentionally supports parallel tests\n\t}\n\tif assertT == nil {\n\t\tassertT = g\n\t}\n\ts.assertT = assertT\n\ts.Assertions = require.New(assertT)\n\ts.ProtoAssertions = protorequire.New(assertT)\n\ts.HistoryRequire = historyrequire.New(assertT)\n}\n\n// T returns the *testing.T, panicking if the guard has been sealed.\nfunc (s *Suite[T]) T() *testing.T {\n\tif s.guardT.hasSubtests.Load() {\n\t\tpanic(\"parallelsuite: do not call T() after Run(); use the subtest callback's parameter instead\")\n\t}\n\treturn s.guardT.T\n}\n\n// TB returns the underlying test handle.\nfunc (s *Suite[T]) TB() testing.TB {\n\treturn s.T()\n}\n\n// Require returns assertions bound to the active test or await attempt.\nfunc (s *Suite[T]) Require() *require.Assertions {\n\treturn s.Assertions\n}\n\n// AssertionT returns the active assertion target.\nfunc (s *Suite[T]) AssertionT() require.TestingT {\n\treturn s.assertT\n}","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/temporalio/temporal/blob/bde624efd13fbd3843654058db6d9c716166318b/common/testing/parallelsuite/suite.go#L85-L121","documentation":"Suite.T() returns the parent *testing.T but is sealed once Run() has been called, at which point subtests receive their own t. Calling T() after Run() panics to prevent using the parent handle concurrently with running subtests, which would be a data race / invalid test failure reporting.","triggerScenarios":"Calling s.T() (directly or via TB, Context, Awaitf, AwaitTruef helpers) after s.Run(...) has started its subtests.","commonSituations":"Goroutines spawned in suite setup that later call s.Context()/s.Awaitf() after Run() started; deferred code calling s.T() during teardown; code written before a suite was converted to parallelsuite.","solutions":["Pass the subtest callback's t parameter into any goroutine/helper needing a test handle instead of calling s.T().","Move T()/Context()/Awaitf() calls to before Run() if they belong to setup.","Use s.TB() or other unsealed accessors if only a testing.TB is needed and permitted."],"exampleFix":"// before\nsuite.Run(\"case\", func(t *parallelsuite.T) { ... })\ntt := suite.T() // panics\n// after\nsuite.Run(\"case\", func(t *parallelsuite.T) {\n\ttt := t.T()\n\t...\n})","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"// inside a Run callback, use the parameter instead of s.T():\nsuite.Run(\"case\", func(t *parallelsuite.T) {\n\ttt := t.T() // safe\n\t_ = tt\n})","tryCatchPattern":null,"preventionTips":["Call s.T()/Context()/Awaitf only during setup, before Run().","Pass the subtest t into spawned goroutines that need test APIs."],"tags":["testing","parallelsuite","concurrency","test-lifecycle"],"backgroundTag":"test-handle-used-after-run","analyzedSha":"bde624efd13fbd3843654058db6d9c716166318b","analyzedAt":"2026-09-01T07:18:39.080Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}