# Extend Visibility Timeout (/learn/queues/how-to/extend-visibility)



## When to Extend Visibility [#when-to-extend-visibility]

Some tasks take longer than expected. Instead of setting an excessively long initial visibility timeout, you can extend the timeout mid-processing to prevent the message from being redelivered.

<Mermaid
  chart="sequenceDiagram
    participant C as Consumer
    participant Q as Queue
    C->>Q: Receive (visibility=60s)
    Note over C: Start processing
    Note over C: 45s elapsed...
    C->>Q: ExtendVisibility(+60s)
    Note over Q: Timeout reset to 60s from now
    Note over C: Continue processing
    C->>Q: Ack
    Note over Q: Message removed"
/>

*Extending the timeout before it lapses keeps a slow task hidden from other consumers until the consumer acknowledges.*

## Steps [#steps]

<Steps>
  <Step>
    ### Receive with Initial Visibility [#receive-with-initial-visibility]

    <Tabs groupId="language" items="['Go', 'Python', 'Node.js', 'Java', 'C#', 'Kotlin', 'C++', 'Rust', 'Ruby', 'Elixir']">
      <Tab value="Go">
        ```go title="extend_visibility.go"
        resp, err := client.PollQueue(ctx, &kubemq.PollRequest{
            Channel:            "orders",
            MaxItems:           1,
            WaitTimeoutSeconds: 5,
            VisibilitySeconds:  60,
        })
        if err != nil {
            log.Fatal(err)
        }
        ```
      </Tab>

      <Tab value="Python">
        ```python title="extend_visibility.py"
        response = client.receive_queue_messages(
            channel="orders",
            max_messages=1,
            wait_timeout_in_seconds=5,
            visibility_seconds=60,
        )
        ```
      </Tab>

      <Tab value="Node.js">
        ```typescript title="extend_visibility.ts"
        const messages = await client.receiveQueueMessages({
          channel: 'orders',
          maxMessages: 1,
          waitTimeoutSeconds: 5,
          visibilitySeconds: 60,
        });
        ```
      </Tab>

      <Tab value="Java">
        ```java title="ExtendVisibility.java"
        ReceiveQueueMessagesResponse response = client.receiveQueueMessages(
            ReceiveQueueMessagesRequest.builder()
                .channel("orders")
                .maxMessages(1)
                .waitTimeoutSeconds(5)
                .visibilitySeconds(60)
                .build());
        ```
      </Tab>

      <Tab value="C#">
        ```csharp title="ExtendVisibility.cs"
        var response = await client.ReceiveQueueMessagesAsync(new ReceiveQueueMessagesRequest
        {
            Channel = "orders",
            MaxMessages = 1,
            WaitTimeoutSeconds = 5,
            VisibilitySeconds = 60,
        });
        ```
      </Tab>

      <Tab value="Kotlin">
        ```kotlin title="ExtendVisibility.kt"
        val response = client.receiveQueueMessages(
            channel = "orders",
            maxMessages = 1,
            waitTimeoutSeconds = 5,
            visibilitySeconds = 60
        )
        ```
      </Tab>

      <Tab value="C++">
        ```cpp title="extend_visibility.cpp"
        auto response = client.receiveQueueMessages("orders", 1, 5, false, 60);
        ```
      </Tab>

      <Tab value="Rust">
        ```rust title="extend_visibility.rs"
        // Poll the queue with explicit ack control (auto_ack = false).
        let (response, mut receiver) = client
            .poll_queue(PollRequest {
                channel: "orders".to_string(),
                max_items: 1,
                wait_timeout_seconds: 5,
                auto_ack: false,
            })
            .await?;
        ```
      </Tab>

      <Tab value="Ruby">
        ```ruby title="extend_visibility.rb"
        receiver = client.create_downstream_receiver
        response = receiver.poll(
          KubeMQ::Queues::QueuePollRequest.new(
            channel: 'orders',
            max_items: 1,
            wait_timeout: 5,
            auto_ack: false
          )
        )
        ```
      </Tab>

      <Tab value="Elixir">
        ```elixir title="extend_visibility.exs"
        {:ok, poll} =
          KubeMQ.Client.poll_queue(client,
            channel: "orders",
            max_items: 1,
            wait_timeout: 5_000,
            auto_ack: false
          )
        ```
      </Tab>
    </Tabs>
  </Step>

  <Step>
    ### Process and Extend When Needed [#process-and-extend-when-needed]

    Before the visibility timeout expires, extend it to get more processing time.

    <Tabs groupId="language" items="['Go', 'Python', 'Node.js', 'Java', 'C#', 'Kotlin', 'C++', 'Rust', 'Ruby', 'Elixir']">
      <Tab value="Go">
        ```go
        for _, m := range resp.Messages {
            fmt.Printf("Processing: %s\n", string(m.Message.Body))

            // Phase 1: Quick validation
            time.Sleep(10 * time.Second)

            // Need more time — extend by 60 more seconds
            if err := m.ExtendVisibility(60); err != nil {
                log.Printf("Failed to extend visibility: %v", err)
            }
            fmt.Println("Visibility extended by 60 seconds")

            // Phase 2: Heavy processing
            time.Sleep(40 * time.Second)
        }
        resp.AckAll()
        fmt.Println("Done — all messages acknowledged")
        ```
      </Tab>

      <Tab value="Python">
        ```python
        for msg in response.messages:
            print(f"Processing: {msg.body.decode('utf-8')}")

            # Phase 1: Quick validation
            time.sleep(10)

            # Need more time — extend by 60 more seconds
            msg.extend_visibility(60)
            print("Visibility extended by 60 seconds")

            # Phase 2: Heavy processing
            time.sleep(40)
            msg.ack()

        print("Done — all messages acknowledged")
        ```
      </Tab>

      <Tab value="Node.js">
        ```typescript
        for (const msg of messages) {
          console.log('Processing:', new TextDecoder().decode(msg.body));

          // Phase 1: Quick validation
          await new Promise((r) => setTimeout(r, 10000));

          // Need more time — extend by 60 more seconds
          await msg.extendVisibility(60);
          console.log('Visibility extended by 60 seconds');

          // Phase 2: Heavy processing
          await new Promise((r) => setTimeout(r, 40000));
          await msg.ack();
        }
        console.log('Done — all messages acknowledged');
        ```
      </Tab>

      <Tab value="Java">
        ```java
        for (QueueMessageReceived msg : response.getMessages()) {
            System.out.println("Processing: " + new String(msg.getBody()));

            Thread.sleep(10_000);

            msg.extendVisibility(60);
            System.out.println("Visibility extended by 60 seconds");

            Thread.sleep(40_000);
            msg.ack();
        }
        System.out.println("Done — all messages acknowledged");
        ```
      </Tab>

      <Tab value="C#">
        ```csharp
        foreach (var msg in response.Messages)
        {
            Console.WriteLine($"Processing: {Encoding.UTF8.GetString(msg.Body.Span)}");

            await Task.Delay(10_000);

            await msg.ExtendVisibilityAsync(60);
            Console.WriteLine("Visibility extended by 60 seconds");

            await Task.Delay(40_000);
            await msg.AckAsync();
        }
        Console.WriteLine("Done — all messages acknowledged");
        ```
      </Tab>

      <Tab value="Kotlin">
        ```kotlin
        for (msg in response.messages) {
            println("Processing: ${String(msg.body)}")

            Thread.sleep(10_000)

            msg.extendVisibility(60)
            println("Visibility extended by 60 seconds")

            Thread.sleep(40_000)
            msg.ack()
        }
        println("Done — all messages acknowledged")
        ```
      </Tab>

      <Tab value="C++">
        ```cpp
        for (const auto& msg : response.messages) {
            std::cout << "Processing: " << msg.body << std::endl;

            std::this_thread::sleep_for(std::chrono::seconds(10));

            msg.extendVisibility(60);
            std::cout << "Visibility extended by 60 seconds" << std::endl;

            std::this_thread::sleep_for(std::chrono::seconds(40));
            msg.ack();
        }
        std::cout << "Done — all messages acknowledged" << std::endl;
        ```
      </Tab>

      <Tab value="Rust">
        ```rust
        // Extending an in-flight message's visibility is not yet available in the Rust SDK —
        // the downstream message exposes ack(), nack(), and re_queue() only.
        // Set a generous initial visibility on the poll, then ack once processing completes.
        for msg in &response.messages {
            println!("Processing: {}", String::from_utf8_lossy(&msg.message.body));

            // Phase 1: Quick validation
            tokio::time::sleep(std::time::Duration::from_secs(10)).await;

            // Phase 2: Heavy processing
            tokio::time::sleep(std::time::Duration::from_secs(40)).await;

            msg.ack().await?;
        }
        println!("Done — all messages acknowledged");
        receiver.close().await?;
        ```
      </Tab>

      <Tab value="Ruby">
        ```ruby
        # Extending an in-flight message's visibility is not yet available in the Ruby SDK —
        # each polled message exposes ack and nack, while requeue is settled at the
        # response level via response.requeue_all(channel:).
        # Set a generous initial visibility on the poll, then ack once processing completes.
        response.messages.each do |msg|
          puts "Processing: #{msg.body}"

          # Phase 1: Quick validation
          sleep(10)

          # Phase 2: Heavy processing
          sleep(40)

          msg.ack
        end
        puts 'Done — all messages acknowledged'
        receiver.close
        ```
      </Tab>

      <Tab value="Elixir">
        ```elixir
        # Extending an in-flight message's visibility is not yet available in the Elixir SDK —
        # PollResponse exposes ack_all/1, nack_all/1, and requeue_all/2 only.
        # Set a generous initial visibility on the poll, then ack once processing completes.
        Enum.each(poll.messages, fn msg ->
          IO.puts("Processing: #{msg.body}")

          # Phase 1: Quick validation
          Process.sleep(10_000)

          # Phase 2: Heavy processing
          Process.sleep(40_000)
        end)

        {:ok, _} = KubeMQ.PollResponse.ack_all(poll)
        IO.puts("Done — all messages acknowledged")
        ```
      </Tab>
    </Tabs>
  </Step>
</Steps>

<Callout type="warn">
  Always extend visibility **before** the current timeout expires. Once the timeout lapses, the message may be delivered to another consumer, leading to duplicate processing.
</Callout>

## Next Steps [#next-steps]

<Cards>
  <Card title="Visibility Timeout" href="/learn/queues/how-to/visibility-timeout" description="Set the initial visibility timeout." />

  <Card title="Queue Reference" href="/learn/queues/reference" description="All configuration options and server limits." />
</Cards>
