# Configure Visibility Timeout (/learn/queues/how-to/visibility-timeout)



## How Visibility Timeout Works [#how-visibility-timeout-works]

When a consumer receives a message, it becomes hidden from other consumers for a configurable duration. If the consumer does not acknowledge within the timeout, the message becomes available again for redelivery.

<Mermaid
  chart="sequenceDiagram
    participant Q as Queue
    participant C1 as Consumer 1
    participant C2 as Consumer 2
    C1->>Q: Poll
    Q-->>C1: Deliver message (hidden)
    Note over Q: Visibility timeout starts
    C2->>Q: Poll
    Q-->>C2: No messages (hidden)
    Note over C1: Processing takes too long...
    Note over Q: Timeout expires
    C2->>Q: Poll
    Q-->>C2: Deliver same message (redelivered)
    C2->>Q: Ack"
/>

*A message stays hidden from other consumers until the owner acks or the visibility timeout expires.*

A single message moves through three states while it is being processed. It is **Hidden** from other consumers from the moment it is delivered. An `ack` settles it as **Acked** (removed from the queue). If the timeout expires first, it returns to **Visible** and the next poll redelivers it.

<Mermaid
  chart="stateDiagram-v2
    [*] --> Visible: message enqueued
    Visible --> Hidden: delivered to consumer
    Hidden --> Acked: ack() within timeout
    Hidden --> Visible: visibility timeout expires
    Acked --> [*]"
/>

*Message lifecycle under a visibility timeout: a poll hides the message, an ack settles it, and an expiry returns it for redelivery.*

## Set Visibility Timeout Per Request [#set-visibility-timeout-per-request]

A per-request visibility override is exposed by the **Java** SDK via `QueuesPollRequest.visibilitySeconds`. It overrides the server default (`DefaultVisibilitySeconds`, 60 seconds) for that poll. The other SDKs do not expose a per-request override; messages stay hidden for the server-side default until you `ack`, so acknowledge before that window elapses (or use a language that surfaces the field).

<Tabs groupId="language" items="['Go', 'Python', 'Node.js', 'Java', 'C#', 'Kotlin', 'C++', 'Rust', 'Ruby', 'Elixir']">
  <Tab value="Go">
    ```go title="visibility.go"
    // A per-request visibility override is not exposed in the Go SDK — the server
    // default (DefaultVisibilitySeconds) applies. Ack before it expires.
    resp, err := client.PollQueue(ctx, &kubemq.PollRequest{
        Channel:            "orders",
        MaxItems:           1,
        WaitTimeoutSeconds: 5,
    })
    if err != nil {
        log.Fatal(err)
    }
    for _, dsMsg := range resp.Messages {
        fmt.Printf("Processing: %s\n", string(dsMsg.Message.Body))
    }
    resp.AckAll()
    ```
  </Tab>

  <Tab value="Python">
    ```python title="visibility.py"
    # A per-request visibility override is not exposed in the Python SDK — the server
    # default (DefaultVisibilitySeconds) applies. Ack before it expires.
    response = client.receive_queue_messages(
        channel="orders",
        max_messages=1,
        wait_timeout_in_seconds=5,
    )
    for msg in response.messages:
        print(f"Processing: {msg.body.decode('utf-8')}")
        msg.ack()
    ```
  </Tab>

  <Tab value="Node.js">
    ```typescript title="visibility.ts"
    // A per-request visibility override is not exposed in the Node.js SDK — the server
    // default (DefaultVisibilitySeconds) applies. Ack before it expires.
    const messages = await client.receiveQueueMessages({
      channel: 'orders',
      maxMessages: 1,
      waitTimeoutSeconds: 5,
    });
    for (const msg of messages) {
      console.log('Processing:', new TextDecoder().decode(msg.body));
      await msg.ack();
    }
    ```
  </Tab>

  <Tab value="Java">
    ```java title="Visibility.java"
    // visibilitySeconds is a client-side timer: if the message is not acked or
    // rejected within 120s, the SDK auto-rejects it and the server redelivers.
    QueuesPollRequest pollRequest = QueuesPollRequest.builder()
            .channel("orders")
            .pollMaxMessages(1)
            .pollWaitTimeoutInSeconds(5)
            .autoAckMessages(false)
            .visibilitySeconds(120)
            .build();

    QueuesPollResponse response = client.receiveQueueMessages(pollRequest);

    for (QueueMessageReceived msg : response.getMessages()) {
        System.out.println("Processing (120s visibility): " + new String(msg.getBody()));
        msg.ack();
    }
    ```
  </Tab>

  <Tab value="C#">
    ```csharp title="Visibility.cs"
    // A per-request visibility override is not exposed in the C# SDK — the server
    // default (DefaultVisibilitySeconds) applies. Ack before it expires.
    var response = await client.ReceiveQueueMessagesAsync(new QueuePollRequest
    {
        Channel = "orders",
        MaxMessages = 1,
        WaitTimeoutSeconds = 5,
        AutoAck = false,
    });
    foreach (var msg in response.Messages)
    {
        Console.WriteLine($"Processing: {Encoding.UTF8.GetString(msg.Body.Span)}");
        await msg.AckAsync();
    }
    ```
  </Tab>

  <Tab value="Kotlin">
    ```kotlin title="Visibility.kt"
    // A per-request visibility override is not exposed in the Kotlin SDK — the server
    // default (DefaultVisibilitySeconds) applies. Ack before it expires.
    val response = client.receiveQueuesMessages {
        channel = "orders"
        maxItems = 1
        waitTimeoutMs = 5000
        autoAck = false
    }
    for (msg in response.messages) {
        println("Processing: ${String(msg.body)}")
        msg.ack()
    }
    ```
  </Tab>

  <Tab value="C++">
    ```cpp title="visibility.cpp"
    // A per-request visibility override is not exposed in the C++ SDK — the server
    // default (DefaultVisibilitySeconds) applies. Ack before it expires.
    kubemq::PollRequest poll_req;
    poll_req.channel = "orders";
    poll_req.max_items = 1;
    poll_req.wait_timeout_seconds = 5;

    auto poll_result = client->PollQueue(poll_req);
    for (const auto& dm : poll_result->messages()) {
        std::cout << "Processing: " << dm.message().body() << std::endl;
        dm.Ack();
    }
    ```
  </Tab>

  <Tab value="Rust">
    ```rust title="visibility.rs"
    // A per-request visibility override is not exposed in the Rust SDK — the server
    // default (DefaultVisibilitySeconds) applies. Ack before it expires.
    let mut receiver = client.new_queue_downstream_receiver().await?;
    let poll = PollRequest {
        channel: "orders".to_string(),
        max_items: 1,
        wait_timeout_seconds: 5,
        auto_ack: false,
    };
    let response = receiver.poll(poll).await?;
    for msg in &response.messages {
        println!("Processing: {}", String::from_utf8_lossy(&msg.message.body));
    }
    response.ack_all().await?;
    ```
  </Tab>

  <Tab value="Ruby">
    ```ruby title="visibility.rb"
    # A per-request visibility override is not exposed in the Ruby SDK — the server
    # default (DefaultVisibilitySeconds) applies. Ack before it expires.
    # Per-message ack requires the streaming receiver, not the unary receive call.
    receiver = client.create_downstream_receiver
    request = KubeMQ::Queues::QueuePollRequest.new(
      channel: 'orders',
      max_items: 1,
      wait_timeout: 5
    )
    response = receiver.poll(request)
    response.messages.each do |m|
      puts "Processing: #{m.body}"
      m.ack
    end
    receiver.close
    ```
  </Tab>

  <Tab value="Elixir">
    ```elixir title="visibility.exs"
    # A per-request visibility override is not exposed in the Elixir SDK — the server
    # default (DefaultVisibilitySeconds) applies. Ack before it expires.
    {:ok, poll} =
      KubeMQ.Client.poll_queue(client,
        channel: "orders",
        max_items: 1,
        wait_timeout: 5_000
      )

    Enum.each(poll.messages, fn msg ->
      IO.puts("Processing: #{msg.body}")
    end)

    {:ok, _} = KubeMQ.PollResponse.ack_all(poll)
    ```
  </Tab>
</Tabs>

## Best Practices [#best-practices]

| Guideline                                                               | Recommendation                     |
| ----------------------------------------------------------------------- | ---------------------------------- |
| Set timeout to 2-3x expected processing time                            | Prevents premature redelivery      |
| Use shorter timeouts for fast operations                                | Reduces delay when consumers crash |
| Use longer timeouts for heavy processing                                | Prevents duplicate work            |
| Consider [extending visibility](/learn/queues/how-to/extend-visibility) | For variable-duration tasks        |

<Callout type="info">
  The maximum visibility timeout is controlled by the server setting `MaxVisibilitySeconds` (default: 43,200 seconds / 12 hours). The default when not specified is `DefaultVisibilitySeconds` (60 seconds).
</Callout>

## Next Steps [#next-steps]

<Cards>
  <Card title="Extend Visibility" href="/learn/queues/how-to/extend-visibility" description="Extend the processing window for long-running tasks." />

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