'Chromedp with Golang has deadline exceeded if element doesnt exist. Is there a way to extend the context timeout after a deadline?

I have a function that uses chromedp to check if there is a acknowledge modal that pops up sometimes. If it pops up the function works but if the context deadline is extended it will still say that the deadline exceeded. Oddly, if the deadline is reduced in other Runs it works.

func check(page string) {
    opts := append(chromedp.DefaultExecAllocatorOptions[:],
        chromedp.UserAgent("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.106 Safari/537.36"),
        chromedp.Flag("enable-automation", false),
        chromedp.Flag("headless", false),
    )
    ctx, cancel := chromedp.NewExecAllocator(context.Background(), opts...)
    defer cancel()
    ctx, cancel = chromedp.NewContext(ctx)
    defer cancel()

    ctx, cancel = context.WithTimeout(ctx, 10*time.Second)
    defer cancel()

    err := chromedp.Run(ctx,
        chromedp.Navigate(page),
        chromedp.WaitReady("body", chromedp.ByQuery),
        chromedp.Click(`#ackBtn`, chromedp.ByID),
    )
    if err != nil {
        fmt.Println("Didn't find Ack")
        //return

    }

    ctx, cancel = context.WithTimeout(ctx, 30*time.Second)
    defer cancel()

    err2 := chromedp.Run(ctx,

        chromedp.Click(`#options > div:nth-child(1) > div.c-card__column2 > a.c-card__btn.btn-new.btn-color-blue.btn-size-xxlarge.btn-width-auto.btn-max-width`, chromedp.ByID),
    )
    if err2 != nil {
        fmt.Println("exited on error", err)

    }

}



Solution 1:[1]

ctx, cancel = context.WithTimeout(ctx, 30*time.Second)

Please note that this statement does not extend the timeout of the context. A child context created from a time out context is time out immediately.

See the test below:

func TestTimeoutContext(t *testing.T) {
    ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond)
    defer cancel()
    time.Sleep(2 * time.Millisecond)
    err := ctx.Err()
    if err != context.DeadlineExceeded {
        t.Errorf("got %v; want %v", err, context.DeadlineExceeded)
    }

    ctx, cancel = context.WithTimeout(ctx, time.Hour)
    defer cancel()

    err = ctx.Err()
    if err != context.DeadlineExceeded {
        t.Errorf("got %v; want %v", err, context.DeadlineExceeded)
    }
}

I think this is what you want:

func check(page string) {
    opts := append(chromedp.DefaultExecAllocatorOptions[:],
        chromedp.UserAgent("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.106 Safari/537.36"),
        chromedp.Flag("enable-automation", false),
        chromedp.Flag("headless", false),
    )
    ctx, cancel := chromedp.NewExecAllocator(context.Background(), opts...)
    defer cancel()
    pageCtx, cancel := chromedp.NewContext(ctx)
    defer cancel()

    // pageCtx is used to open the page
    if err := chromedp.Run(pageCtx,
        chromedp.Navigate(page),
    ); err != nil {
        return
    }

    // ackCtx is created from pageCtx.
    // when ackCtx exceeds the deadline, pageCtx is not affected.
    ackCtx, cancel := context.WithTimeout(pageCtx, 10*time.Second)
    defer cancel()

    err := chromedp.Run(ackCtx,
        chromedp.WaitReady("body", chromedp.ByQuery),
        chromedp.Click(`#ackBtn`, chromedp.ByID),
    )
    if err != nil {
        fmt.Println("Didn't find Ack")
        //return
    }

    // create another context from pageCtx to make sure the action finished in
    // 30 seconds. If you don't need this behavior, you can use pageCtx directly.
    clickCtx, cancel := context.WithTimeout(pageCtx, 30*time.Second)
    defer cancel()

    err2 := chromedp.Run(clickCtx,
        chromedp.Click(`#options > div:nth-child(1) > div.c-card__column2 > a.c-card__btn.btn-new.btn-color-blue.btn-size-xxlarge.btn-width-auto.btn-max-width`, chromedp.ByID),
    )
    if err2 != nil {
        fmt.Println("exited on error", err)

    }
}

Refer to https://go.dev/blog/context for the idea behind the design of context.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Zeke Lu