commaai/openpilot · warning · ValueError

QR URL is too long

Error message

QR URL is too long

What it means

make_texture() searches QR versions 1..20 for one whose capacity fits the URL (with a 4-bit or 16-bit length prefix). If even version 20 (177x177 modules) cannot hold len(data), it raises ValueError 'QR URL is too long'.

Source

Thrown at openpilot/common/qrcode.py:206

      for vert in range(self.size):
        y = self.size - 1 - vert if upward else vert
        for x in (right, right - 1):
          if not self.function[y][x]:
            self.modules[y][x] = bool(next(bits, 0))
      upward = not upward
      right -= 2


def make_texture(data: str, inverted: bool = False) -> rl.Texture:
  """Render a URL as the RGBA QR texture used by the UI. The texture upload
  copies the pixels, so the intermediate image/array don't need to outlive it."""
  raw = data.encode()
  for version in range(1, 21):
    count_bits = 8 if version <= 9 else 16
    if 4 + count_bits + len(raw) * 8 <= _capacity(version) * 8:
      break
  else:
    raise ValueError("QR URL is too long")
  modules = np.pad(_Qr(version, raw).modules, 0 if inverted else 4)
  modules = np.repeat(np.repeat(modules, 10, axis=0), 10, axis=1)
  gray = ((modules == inverted) * 255).astype(np.uint8)
  img_array = np.dstack((gray, gray, gray, np.full_like(gray, 255)))

  rl_image = rl.Image()
  rl_image.data = rl.ffi.cast("void *", img_array.ctypes.data)
  rl_image.width = img_array.shape[1]
  rl_image.height = img_array.shape[0]
  rl_image.mipmaps = 1
  rl_image.format = rl.PixelFormat.PIXELFORMAT_UNCOMPRESSED_R8G8B8A8
  return rl.load_texture_from_image(rl_image)

View on GitHub (pinned to 516ec1e682)

Solutions

  1. Shorten the URL (use a redirect/short-link service or trim query parameters)
  2. Verify you are passing the URL string, not an encoded payload object
  3. If long data is required, split it across multiple QR renders or use a data channel other than QR

Example fix

// before
make_texture(f"https://example.com/connect?token={huge_jwt}")

// after
make_texture(f"https://example.com/c/{short_code}")
Defensive patterns

Strategy: validation

Validate before calling

MAX_QR_BYTES = 1273  # version 20, low EC, byte mode (approx)

def qr_encodable(data: str) -> bool:
    return len(data.encode()) <= MAX_QR_BYTES

Try / catch

try:
    tex = make_texture(url)
except ValueError:
    tex = make_texture(shorten(url))

Prevention

When it happens

Trigger: Calling make_texture(url) where the encoded URL exceeds the byte capacity of QR version 20 at the chosen error-correction level — roughly 1-2 KB depending on level.

Common situations: Passing a URL with huge query strings, embedded tokens, or accidentally passing a whole JSON payload instead of a URL.


AI-assisted analysis of commaai/openpilot@516ec1e682 (2026-08-15). Data as JSON: /api/errors/b153b526a18ea9e6. Report an issue: GitHub.