Skip to content
Back to skills

Psycopg2 Like Percent Escape

ASecurity

Fix psycopg2 "IndexError: tuple index out of range" when using LIKE with parameterized queries. Use when: (1) cursor.execute() fails with IndexError on a query containing LIKE '%pattern%', (2) SQL LIKE wildcards conflict with psycopg2 %s parameter placeholders, (3) Query works in psql but fails in Python. The % character has dual meaning: SQL LIKE wildcard AND psycopg2's parameter substitution marker.

  • 265 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added May 27, 2026
developmentpythongosqldjangogitapidocumentation

Works with

  • claude code
  • cursor
  • api

Security analysis

A100/100

Scanned May 27, 2026

npx -y skills add divinevideo/divine-mobile --skill psycopg2-like-percent-escape --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Psycopg2 Like Percent Escape?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Psycopg2 Like Percent Escape
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/divinevideo-psycopg2-like-percent-escape/badge)](https://www.skillsdirectory.com/skills/divinevideo-psycopg2-like-percent-escape)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: psycopg2-like-percent-escape
description: |
  Fix psycopg2 "IndexError: tuple index out of range" when using LIKE with parameterized queries.
  Use when: (1) cursor.execute() fails with IndexError on a query containing LIKE '%pattern%',
  (2) SQL LIKE wildcards conflict with psycopg2 %s parameter placeholders, (3) Query works
  in psql but fails in Python. The % character has dual meaning: SQL LIKE wildcard AND
  psycopg2's parameter substitution marker.
author: Claude Code
version: 1.0.0
date: 2026-01-29
---

# psycopg2 LIKE Clause Percent Sign Escaping

## Problem

When using psycopg2 with parameterized queries containing SQL LIKE patterns, the `%` character
causes conflicts. The `%` is used both as:
1. SQL LIKE wildcard (e.g., `'%pattern%'`)
2. psycopg2's parameter placeholder marker (e.g., `%s`)

This results in confusing errors like `IndexError: tuple index out of range` because psycopg2
interprets `%c` in `%cdn` as a format specifier.

## Context / Trigger Conditions

- `IndexError: tuple index out of range` from `cursor.execute()`
- Query contains hardcoded LIKE pattern: `WHERE column LIKE '%something%'`
- Query also uses `%s` parameters for other values
- Query works in psql or pgAdmin but fails in Python

Example failing code:
```python
cursor.execute("""
    SELECT * FROM vines
    WHERE url LIKE '%cdn.vine.co%'
    LIMIT %s OFFSET %s
""", (1000, 0))
# IndexError: tuple index out of range
```

## Solution

### Option 1: Escape `%` with `%%` (for static patterns)

Double the percent signs in hardcoded LIKE patterns:

```python
cursor.execute("""
    SELECT * FROM vines
    WHERE url LIKE '%%cdn.vine.co%%'
    LIMIT %s OFFSET %s
""", (1000, 0))
```

### Option 2: Pass LIKE pattern as parameter (recommended)

The cleaner approach - pass the entire LIKE pattern as a parameter:

```python
pattern = '%cdn.vine.co%'
cursor.execute("""
    SELECT * FROM vines
    WHERE url LIKE %s
    LIMIT %s OFFSET %s
""", (pattern, 1000, 0))
```

This is the recommended approach because:
- No escaping confusion
- Pattern can be dynamically constructed
- Follows parameterized query best practices

### Option 3: Use psycopg2.sql module for complex cases

For dynamic SQL construction:

```python
from psycopg2 import sql

query = sql.SQL("""
    SELECT * FROM {table}
    WHERE url LIKE %s
""").format(table=sql.Identifier('vines'))

cursor.execute(query, ('%cdn.vine.co%',))
```

## Verification

After applying the fix:
1. Query executes without IndexError
2. Results correctly match the LIKE pattern
3. Other `%s` parameters are still substituted correctly

## Example

Before (broken):
```python
def get_vines_by_cdn(db, limit, offset):
    cursor = db.cursor()
    cursor.execute("""
        SELECT vine_id, url FROM discovered_vines
        WHERE url LIKE '%cdn.vine.co%'
        ORDER BY created_at
        LIMIT %s OFFSET %s
    """, (limit, offset))
    return cursor.fetchall()
```

After (fixed with Option 1):
```python
def get_vines_by_cdn(db, limit, offset):
    cursor = db.cursor()
    cursor.execute("""
        SELECT vine_id, url FROM discovered_vines
        WHERE url LIKE '%%cdn.vine.co%%'
        ORDER BY created_at
        LIMIT %s OFFSET %s
    """, (limit, offset))
    return cursor.fetchall()
```

After (fixed with Option 2 - recommended):
```python
def get_vines_by_cdn(db, limit, offset):
    cursor = db.cursor()
    cdn_pattern = '%cdn.vine.co%'
    cursor.execute("""
        SELECT vine_id, url FROM discovered_vines
        WHERE url LIKE %s
        ORDER BY created_at
        LIMIT %s OFFSET %s
    """, (cdn_pattern, limit, offset))
    return cursor.fetchall()
```

## Notes

- This issue only affects parameterized queries with `%s` placeholders
- Raw SQL strings without parameters don't have this problem
- The `%%` escape only works when the query uses psycopg2's parameter substitution
- Django's ORM handles this automatically; this is a raw SQL issue
- psycopg3 uses `$1, $2` style placeholders, avoiding this conflict entirely

## Related Issues

- Searching for literal `%` in data requires additional escaping with `ESCAPE` clause
- Similar issues can occur with `_` (single character wildcard) if using `%_` pattern

## References

- [psycopg2 Basic Module Usage](https://www.psycopg.org/docs/usage.html)
- [psycopg2 sql Module Documentation](https://www.psycopg.org/docs/sql.html)
- [PostgreSQL Pattern Matching](https://www.postgresql.org/docs/current/functions-matching.html)
- [psycopg2 Issue #825 - Percent sign escaping](https://github.com/psycopg/psycopg2/issues/825)

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…