{"record":{"id":"7e48a24fa56770d6","repo":"temporalio/temporal","slug":"parallelsuite-assertion-called-on-q-after-run","errorCode":null,"errorMessage":"parallelsuite: assertion called on %q after Run() was called; use the callback parameter's assertions inside Run() instead","messagePattern":"parallelsuite: assertion called on %q after Run\\(\\) was called; use the callback parameter's assertions inside Run\\(\\) instead","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"common/testing/parallelsuite/guard.go","lineNumber":20,"sourceCode":"\nimport (\n\t\"fmt\"\n\t\"sync/atomic\"\n\t\"testing\"\n)\n\n// guardT is a [require.TestingT] wrapper that prevents assertions after Run() is called.\n//\n// Assertions before Run() are allowed; after Run() any assertion panics.\ntype guardT struct {\n\t*testing.T\n\tname        string\n\thasSubtests atomic.Bool\n}\n\nfunc (g *guardT) Helper() {\n\tif g.hasSubtests.Load() {\n\t\tpanic(fmt.Sprintf(\n\t\t\t\"parallelsuite: assertion called on %q after Run() was called; \"+\n\t\t\t\t\"use the callback parameter's assertions inside Run() instead\",\n\t\t\tg.name,\n\t\t))\n\t}\n\tg.T.Helper()\n}\n\nfunc (g *guardT) Errorf(format string, args ...any) {\n\tif g.hasSubtests.Load() {\n\t\tg.Helper() // panics with clear message\n\t}\n\tg.T.Errorf(format, args...)\n}\n\nfunc (g *guardT) FailNow() {\n\tif g.hasSubtests.Load() {\n\t\tg.Helper() // panics with clear message","sourceCodeStart":2,"sourceCodeEnd":38,"githubUrl":"https://github.com/temporalio/temporal/blob/bde624efd13fbd3843654058db6d9c716166318b/common/testing/parallelsuite/guard.go#L2-L38","documentation":"parallelsuite's guardT wraps a testing.T and seals it once Suite.Run() spawns subtests. Any assertion method invoked on the parent guard after Run() started panics, because assertions must use the subtest callback's own t to avoid cross-subtest races.","triggerScenarios":"Calling s.Errorf(...) (or FailNow, which routes through Helper) on the suite's parent guard after s.Run(name, func(t *parallelsuite.T){...}) has been invoked.","commonSituations":"Deferring cleanup assertions at suite setup time that fire after Run(); helper methods capturing the suite's t in a closure and calling it inside/after Run(); assertions made between subtests.","solutions":["Move the assertion inside the Run() callback and use the callback's t parameter.","Assert before calling Run(), or capture results and assert within each subtest.","Restructure shared post-subtest checks so each subtest performs its own assertions on its own t."],"exampleFix":"// before\nsuite.Run(\"case\", func(t *parallelsuite.T) { ... })\nsuite.Errorf(\"done\") // panics\n// after\nsuite.Run(\"case\", func(t *parallelsuite.T) {\n\t...\n\tt.Errorf(\"done\")\n})","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"func canAssertOnSuite(s *parallelsuite.Suite[X]) bool {\n\treturn !s.TB().(*guardT).hasSubtests.Load() // or use suite API before Run()\n}","tryCatchPattern":null,"preventionTips":["Always use the Run() callback's t for assertions.","Avoid capturing the suite's parent t in long-lived closures or deferred functions."],"tags":["testing","parallelsuite","concurrency","assertion"],"backgroundTag":"assertion-after-test-run","analyzedSha":"bde624efd13fbd3843654058db6d9c716166318b","analyzedAt":"2026-09-01T07:18:39.080Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}