kovidgoyal/kitty · warning

Too many hyperlinks, discarding hyperlinks in scrollback

Error message

Too many hyperlinks, discarding hyperlinks in scrollback

What it means

The hyperlink id pool hit HYPERLINK_MAX_NUMBER, so kitty garbage-collected hyperlinks referenced only by scrollback lines to free ids; scrollback links may stop being clickable. Rendering is unaffected.

Source

Thrown at kitty/hyperlink.c:149

    _screen_garbage_collect_hyperlink_pool(screen, true);
}


hyperlink_id_type
get_id_for_hyperlink(Screen *screen, const char *id, const char *url) {
    if (!url) return 0;
    HyperLinkPool *pool = (HyperLinkPool *)screen->hyperlink_pool;
    static char key[MAX_KEY_LEN] = {0};
    int keylen = snprintf(key, MAX_KEY_LEN - 1, "%.*s:%s", MAX_ID_LEN, id ? id : "", url);
    if (keylen < 0) keylen = strlen(key);
    else keylen = MIN(keylen, MAX_KEY_LEN - 2); // snprintf returns how many chars it would have written in case of truncation
    key[keylen] = 0;
    hyperlink_map_itr itr = vt_get(&pool->map, key);
    if (!vt_is_end(itr)) return itr.data->val;
    if (pool->array.count >= HYPERLINK_MAX_NUMBER - 1) {
        screen_garbage_collect_hyperlink_pool(screen);
        if (pool->array.count >= HYPERLINK_MAX_NUMBER - 128) {
            log_error("Too many hyperlinks, discarding hyperlinks in scrollback");
            _screen_garbage_collect_hyperlink_pool(screen, false);
            if (pool->array.count >= HYPERLINK_MAX_NUMBER) {
                log_error("Too many hyperlinks, discarding hyperlink: %s", key);
                return 0;
            }
        }
    }
    if (!pool->array.count) pool->array.count = 1; // First id must be 1
    ensure_space_for(&(pool->array), items, hyperlink, pool->array.count + 1, capacity, 256, false);
    hyperlink_id_type new_id = pool->array.count++;
    pool->array.items[new_id] = dupstr(key, keylen);
    if (vt_is_end(vt_insert(&pool->map, pool->array.items[new_id], new_id))) fatal("Out of memory");
    // If there have been a lot of hyperlink adds do a garbage collect so as
    // not to leak too much memory over unused hyperlinks
    if (++pool->adds_since_last_gc > 8192) screen_garbage_collect_hyperlink_pool(screen);
    return new_id;
}

View on GitHub (pinned to 6d5d0c4406)

Solutions

  1. Reduce unique URIs the application emits (reuse ids via OSC 8 ; id=...)
  2. Clear scrollback (ctrl+shift+delete / clear_terminal scrollback)
  3. Update kitty - pool sizing and GC heuristics improve between versions
  4. Report/fix the application that floods unique hyperlinks

Example fix

# before (new id minted per link)
printf '\033]8;;%s\033\\%s\033]8;;\033\\\n' "$url" "$text"
# after (reuse explicit id)
printf '\033]8;id=u1;%s\033\\%s\033]8;;\033\\\n' "$url" "$text"
Defensive patterns

Strategy: fallback

Validate before calling

# when emitting many links, reuse an explicit id instead of minting new ones
printf '\033]8;id=%s;%s\033\\%s\033]8;;\033\\' "$link_id" "$url" "$text"

Prevention

When it happens

Trigger: A program emits enormous numbers of distinct OSC 8 hyperlink URIs; after garbage collection the pool is still >= MAX-128, so scrollback hyperlinks are forcibly discarded.

Common situations: Terminal apps printing a unique URL per line (log viewers, chat clients, linkify-everything tools) with large scrollback buffers.

Related errors


AI-assisted analysis of kovidgoyal/kitty@6d5d0c4406 (2026-08-27). Data as JSON: /api/errors/5493bb345bb09cb7. Report an issue: GitHub.