litellm - 💡(How to fix) Fix [Bug]: `/key/update` cannot clear `budget_limits` — both empty array and null return HTTP 400

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Error Message

Authentication Error, Unable to match input value to any allowed input type for the field. Parse errors: [Unable to match input value to any allowed input type for the field. Parse errors: [Invalid argument type. budget_limits should be of any of the following types: NullableJsonNullValueInput, Invalid argument type. budget_limits should be of any of the following types: Json], ...]

Root Cause

The field is defined as budget_limits Json? on LiteLLM_VerificationToken (litellm/proxy/schema.prisma).

1. Empty array ([]): In prepare_key_update_data (litellm/proxy/management_endpoints/key_management_endpoints.py):

if "budget_limits" in non_default_values and non_default_values["budget_limits"]:
    ...
    non_default_values["budget_limits"] = json.dumps(initialized_windows)

The and non_default_values["budget_limits"] guard is falsy for [], so the json.dumps(...) serialization is skipped. The raw Python list then reaches PrismaClient.jsonify_object (litellm/proxy/utils.py), which only serializes dict values:

for k, v in db_data.items():
    if isinstance(v, dict):
        db_data[k] = json.dumps(v)

A list is left untouched, so a raw Python [] is handed to Prisma's Json? column, which rejects it (should be of any of the following types: Json).

2. Null: A raw Python None reaches the same Json? column. prisma-client-python (prisma==0.11.0) has no DbNull/JsonNull sentinel for writing SQL NULL to a Json? column — see RobertCraigie/prisma-client-py#714 — so Prisma reports A value is required but not set.

This same limitation is already worked around elsewhere in the codebase:

  • litellm/proxy/memory/memory_endpoints.py (_serialize_metadata_for_prisma) encodes metadata: null as the JSON literal string "null" via json.dumps(None) so it passes the Json? column as jsonb 'null'.
  • litellm/proxy/common_utils/reset_budget_job.py notes the same #714 limitation and uses query_raw for null-filtering.

But /key/update applies neither workaround to budget_limits.

Fix Action

Fix / Workaround

But /key/update applies neither workaround to budget_limits.

Code Example

if "budget_limits" in non_default_values and non_default_values["budget_limits"]:
    ...
    non_default_values["budget_limits"] = json.dumps(initialized_windows)

---

for k, v in db_data.items():
    if isinstance(v, dict):
        db_data[k] = json.dumps(v)

---

if "budget_limits" in non_default_values:
    raw_windows = non_default_values["budget_limits"]
    if raw_windows:
        # ... existing reset_at initialization + json.dumps ...
        non_default_values["budget_limits"] = json.dumps(initialized_windows)
    else:
        # [] or None -> store JSON null, matching memory_endpoints' approach
        non_default_values["budget_limits"] = json.dumps(None)

---

curl -sX POST "$LITELLM_URL/key/generate" \
  -H "Authorization: Bearer $LITELLM_MASTER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"budget_limits": [{"budget_duration": "7d", "max_budget": 25}]}'

---

curl -sX POST "$LITELLM_URL/key/update" \
  -H "Authorization: Bearer $LITELLM_MASTER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"key": "sk-...", "budget_limits": []}'

---

curl -sX POST "$LITELLM_URL/key/update" \
  -H "Authorization: Bearer $LITELLM_MASTER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"key": "sk-...", "budget_limits": null}'

---

# Empty array (`budget_limits: []`):

Authentication Error, Unable to match input value to any allowed input type for the field. Parse errors: [Unable to match input value to any allowed input type for the field. Parse errors: [Invalid argument type. `budget_limits` should be of any of the following types: `NullableJsonNullValueInput`, Invalid argument type. `budget_limits` should be of any of the following types: `Json`], ...]

# Null (`budget_limits: null`):

Unable to match input value to any allowed input type for the field. Parse errors: [`data.budget_limits`: A value is required but not set, `data.budget_limits`: A value is required but not set], ...
RAW_BUFFERClick to expand / collapse

Check for existing issues

  • I have searched the existing issues and checked that my issue is not a duplicate.

What happened?

There is no way to clear a key's budget_limits (per-key concurrent budget windows) through /key/update. Once a key has any budget_limits windows, removing the last one is impossible via the API:

  • Sending budget_limits: []HTTP 400
  • Sending budget_limits: nullHTTP 400

This makes deleting an individual budget window unrecoverable once it becomes the only remaining window: the client must compute the post-deletion array, and if that array is empty there is no accepted value to send.

Expected behavior

Sending budget_limits: [] or budget_limits: null to /key/update should clear the field (store SQL NULL / empty), mirroring how metadata: null is already handled for the memory endpoints.

Root cause analysis

The field is defined as budget_limits Json? on LiteLLM_VerificationToken (litellm/proxy/schema.prisma).

1. Empty array ([]): In prepare_key_update_data (litellm/proxy/management_endpoints/key_management_endpoints.py):

if "budget_limits" in non_default_values and non_default_values["budget_limits"]:
    ...
    non_default_values["budget_limits"] = json.dumps(initialized_windows)

The and non_default_values["budget_limits"] guard is falsy for [], so the json.dumps(...) serialization is skipped. The raw Python list then reaches PrismaClient.jsonify_object (litellm/proxy/utils.py), which only serializes dict values:

for k, v in db_data.items():
    if isinstance(v, dict):
        db_data[k] = json.dumps(v)

A list is left untouched, so a raw Python [] is handed to Prisma's Json? column, which rejects it (should be of any of the following types: Json).

2. Null: A raw Python None reaches the same Json? column. prisma-client-python (prisma==0.11.0) has no DbNull/JsonNull sentinel for writing SQL NULL to a Json? column — see RobertCraigie/prisma-client-py#714 — so Prisma reports A value is required but not set.

This same limitation is already worked around elsewhere in the codebase:

  • litellm/proxy/memory/memory_endpoints.py (_serialize_metadata_for_prisma) encodes metadata: null as the JSON literal string "null" via json.dumps(None) so it passes the Json? column as jsonb 'null'.
  • litellm/proxy/common_utils/reset_budget_job.py notes the same #714 limitation and uses query_raw for null-filtering.

But /key/update applies neither workaround to budget_limits.

Suggested fix

In prepare_key_update_data, handle the clear case explicitly so an empty list or null is serialized rather than skipped — e.g.:

if "budget_limits" in non_default_values:
    raw_windows = non_default_values["budget_limits"]
    if raw_windows:
        # ... existing reset_at initialization + json.dumps ...
        non_default_values["budget_limits"] = json.dumps(initialized_windows)
    else:
        # [] or None -> store JSON null, matching memory_endpoints' approach
        non_default_values["budget_limits"] = json.dumps(None)

This stores jsonb 'null', which prisma deserializes back to None on read, giving callers a working "clear the windows" path.

Steps to Reproduce

  1. Create a key with a single budget window:
curl -sX POST "$LITELLM_URL/key/generate" \
  -H "Authorization: Bearer $LITELLM_MASTER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"budget_limits": [{"budget_duration": "7d", "max_budget": 25}]}'
  1. Try to clear it with an empty array:
curl -sX POST "$LITELLM_URL/key/update" \
  -H "Authorization: Bearer $LITELLM_MASTER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"key": "sk-...", "budget_limits": []}'

→ 400, Prisma complains the raw list is not valid for the Json? column.

  1. Try to clear it with null:
curl -sX POST "$LITELLM_URL/key/update" \
  -H "Authorization: Bearer $LITELLM_MASTER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"key": "sk-...", "budget_limits": null}'

→ 400, Prisma complains a value is required but not set.

Relevant log output

# Empty array (`budget_limits: []`):

Authentication Error, Unable to match input value to any allowed input type for the field. Parse errors: [Unable to match input value to any allowed input type for the field. Parse errors: [Invalid argument type. `budget_limits` should be of any of the following types: `NullableJsonNullValueInput`, Invalid argument type. `budget_limits` should be of any of the following types: `Json`], ...]

# Null (`budget_limits: null`):

Unable to match input value to any allowed input type for the field. Parse errors: [`data.budget_limits`: A value is required but not set, `data.budget_limits`: A value is required but not set], ...

What part of LiteLLM is this about?

Proxy

What LiteLLM version are you on ?

v1.89.0-rc.1

Twitter / LinkedIn details

No response

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

Sending budget_limits: [] or budget_limits: null to /key/update should clear the field (store SQL NULL / empty), mirroring how metadata: null is already handled for the memory endpoints.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING