Filter Expression can only contain non-primary key attributes

TL;DR — You put a primary key attribute (partition key or sort key — of the table or the index you're querying) inside a FilterExpression. DynamoDB forbids that: key attributes go in the KeyConditionExpression, and a filter can only reference non-key attributes. Move the key condition where it belongs.

What it means

ValidationException: Filter Expression can only contain non-primary key attributes:
Primary key attribute: <name>

# what the engine actually returns, reproduced against DynamoDB Local:
ValidationException: Filter Expression can only contain non-primary key attributes: Primary key attribute: pk

FilterExpression runs after items are read, to discard rows you don't want; KeyConditionExpression runs before, to select which items are read by key. Referencing a partition/sort key in the filter mixes those roles, so DynamoDB rejects it with an HTTP 400 ValidationExceptionclient-side and not retryable until you restructure.

Why it happens

  • A key condition written as a filterFilterExpression: 'sk = :v' where sk is the sort key; it belongs in KeyConditionExpression.
  • Filtering on the index's key — when you Query a GSI/LSI, that index's own partition/sort key are "primary key attributes" for this query and can't appear in the filter.
  • Copy-pasting a scan filter onto a query where one filtered attribute happens to be a key.
  • Trying to add a second condition on the sort key via the filter (e.g. a range) instead of expressing it in the key condition.

How to fix it

  1. Move key conditions into KeyConditionExpression:
    KeyConditionExpression: 'pk = :pk AND begins_with(sk, :prefix)',
    // FilterExpression: only NON-key attributes, e.g. 'status = :active'
  2. Use the right index. If you need to filter/select on an attribute that isn't a key, model it as the partition/sort key of a GSI and query that index by key.
  3. Keep the filter for non-key attributes only — it trims results but still consumes read capacity for every item scanned, so lean on keys/indexes for selection.
  4. Querying a GSI? Remember its key attributes are off-limits in the filter too — condition on them in the key condition.
  5. Audit generated requests. Log both KeyConditionExpression and FilterExpression together — key attributes in the filter are a common copy-paste mistake from Scan code.

Run it in DynoTable

DynoTable's query panel keeps key conditions and filters in separate fields — partition and sort key constraints never land in FilterExpression. Open a table with ⌘K, set the key condition, then add non-key filters; copy the generated request into your SDK.

Use the Query Builder to prototype GSI queries where index keys must stay in KeyConditionExpression. Switch profiles with ⌘P; Test Connection on Settings → Profiles confirms the index exists. See Connect to AWS and Install.

Sources

References

Last verified 2026-07-13 against the official AWS documentation linked above.

Work with DynamoDB without the Console

A fast DynamoDB desktop client that runs the real SQL DynamoDB can’t — JOINs, GROUP BY, aggregates — with visual editing and an AI agent on your own Bedrock keys.

Free 30-day trial, no credit card — then the Free plan with no time limit.