{"record":{"id":"b273fee72f8f679f","repo":"gocolly/colly","slug":"cannot-call-duplicate-queue-run","errorCode":null,"errorMessage":"cannot call duplicate Queue.Run","messagePattern":"cannot call duplicate Queue\\.Run","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"queue/queue.go","lineNumber":137,"sourceCode":"\tif err != nil {\n\t\treturn err\n\t}\n\treturn q.storage.AddRequest(d)\n}\n\n// Size returns the size of the queue\nfunc (q *Queue) Size() (int, error) {\n\treturn q.storage.QueueSize()\n}\n\n// Run starts consumer threads and calls the Collector\n// to perform requests. Run blocks while the queue has active requests\n// The given Storage must not be used directly while Run blocks.\nfunc (q *Queue) Run(c *colly.Collector) error {\n\tq.mut.Lock()\n\tif q.wake != nil && q.running == true {\n\t\tq.mut.Unlock()\n\t\tpanic(\"cannot call duplicate Queue.Run\")\n\t}\n\tq.wake = make(chan struct{})\n\tq.running = true\n\tq.mut.Unlock()\n\n\trequestc := make(chan *colly.Request)\n\tcomplete, errc := make(chan struct{}), make(chan error, 1)\n\tfor i := 0; i < q.Threads; i++ {\n\t\tgo independentRunner(requestc, complete)\n\t}\n\tgo q.loop(c, requestc, complete, errc)\n\tdefer close(requestc)\n\treturn <-errc\n}\n\n// Stop will stop the running queue\nfunc (q *Queue) Stop() {\n\tq.mut.Lock()","sourceCodeStart":119,"sourceCodeEnd":155,"githubUrl":"https://github.com/gocolly/colly/blob/17d1d6ca92bd32a5651f34256bf7a2855c967f65/queue/queue.go#L119-L155","documentation":"Queue.Run blocks while draining the queue, so running it twice concurrently on the same Queue would double-consume requests. The Queue guards this with a mutex and panics with 'cannot call duplicate Queue.Run' if q.running is already true. It is a programmer-error guard, not a recoverable library error.","triggerScenarios":"Calling q.Run(collector) a second time (e.g. from another goroutine or after an initial Run plus re-invocation) while the first Run is still active — q.wake != nil && q.running == true.","commonSituations":"Restarting a queue after adding new URLs without waiting for the first Run to return; accidentally starting Run in multiple goroutines; framework callbacks re-invoking Run; mistaking Run for non-blocking and calling it in a loop.","solutions":["Ensure only one goroutine calls Run per Queue; wait for Run to return before calling it again","If you need concurrent draining, create separate Queue instances per worker group","Recover from the panic only at a top-level boundary, then restructure startup so Run is called exactly once","Queue new URLs before Run starts, or use Queue.AddURL/append before a single Run"],"exampleFix":"// before\ngo queue.Run(c)\n...\ngo queue.Run(c) // panics: duplicate Run\n// after\nif err := queue.Run(c); err != nil { // single blocking call\n    log.Fatal(err)\n}","handlingStrategy":"try-catch","validationCode":"// guard before calling Run: track invocation yourself\nvar runStarted int32\nfunc safeRun(q *queue.Queue, c *colly.Collector) error {\n    if !atomic.CompareAndSwapInt32(&runStarted, 0, 1) {\n        return errors.New(\"Run already invoked\")\n    }\n    return q.Run(c)\n}","typeGuard":null,"tryCatchPattern":"func drain(q *queue.Queue, c *colly.Collector) (err error) {\n    defer func() {\n        if r := recover(); r != nil {\n            if s, ok := r.(string); ok && strings.Contains(s, \"duplicate Queue.Run\") {\n                err = errors.New(\"queue already running\")\n                return\n            }\n            panic(r)\n        }\n    }()\n    return q.Run(c)\n}","preventionTips":["Call Queue.Run exactly once per Queue, from one goroutine","Append all URLs (Queue.AddURL/AddRequest) before Run; do not restart Run mid-flight","Use a sync.Once or atomic flag around Run invocation","Never call Run again inside collector callbacks that Run itself triggers"],"tags":["concurrency","goroutine","panic","queue"],"backgroundTag":"duplicate-run-call","analyzedSha":"17d1d6ca92bd32a5651f34256bf7a2855c967f65","analyzedAt":"2026-08-30T21:32:31.579Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}