Skip to content

fix(proxy): json-encode team budget_limits on /team/new so Prisma accepts multi-window budgets - #31002

Closed
mubashir1osmani wants to merge 1 commit into
BerriAI:litellm_internal_stagingfrom
mubashir1osmani:litellm_team_new_budget_limits_json_encode
Closed

fix(proxy): json-encode team budget_limits on /team/new so Prisma accepts multi-window budgets#31002
mubashir1osmani wants to merge 1 commit into
BerriAI:litellm_internal_stagingfrom
mubashir1osmani:litellm_team_new_budget_limits_json_encode

Conversation

@mubashir1osmani

Copy link
Copy Markdown
Collaborator

Relevant issues

POST /team/new returns a 500 when the request includes multi-window budget_limits. Prisma rejects the create with:

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`, `Json`]

The root cause is that the create path builds complete_team_data_dict = complete_team_data.model_dump(exclude_none=True) and hands it to the team-create call. That dict flows through jsonify_team_object -> jsonify_object, which only json.dumps values that are a dict. budget_limits is a list, so it passes through unencoded and Prisma refuses it because the LiteLLM_TeamTable.budget_limits column is Json?. The update path already serializes it via json.dumps and the key path does the same, so this aligns /team/new with the rest of the codebase.

The fix json-encodes budget_limits on the dict right after model_dump, parallel to the existing router_settings serialization just below it. This is safe against double-encoding: jsonify_object leaves strings alone, TeamRepository._to_model json-loads when the value is a string, proxy_server handles the string case on read, and the reset-budget job tolerates both a list and a json string.

Linear ticket

LIT-3896

Pre-Submission checklist

  • I have added meaningful tests
  • My PR passes all unit tests on make test-unit
  • My PR's scope is as isolated as possible; it only solves 1 specific problem
  • I have requested a Greptile review by commenting @greptileai and received a Confidence Score of at least 4/5 before requesting a maintainer review

CI (LiteLLM team)

  • Branch creation CI run
    Link:

  • CI run for the last commit
    Link:

  • Merge / cherry-pick CI run
    Links:

Screenshots / Proof of Fix

Run a local proxy, then create a team with two budget windows and read it back. Before this change the create returns a 500; after it, the create returns 200 with a team_id and GET /team/info shows both windows persisted with a populated reset_at.

# 1. start the proxy (separate terminal)
python litellm/proxy/proxy_cli.py --config litellm/proxy/dev_config.yaml --detailed_debug --reload --use_v2_migration_resolver 2>&1 | tee litellm.log

# 2. create a team with two budget windows
TEAM_ID=$(curl -s -X POST http://localhost:4000/team/new \
  -H "Authorization: Bearer sk-1234" \
  -H "Content-Type: application/json" \
  -d '{
        "team_alias": "multi-window-team",
        "budget_limits": [
          {"budget_duration": "1d",  "max_budget": 10.0},
          {"budget_duration": "30d", "max_budget": 100.0}
        ]
      }' | tee /dev/stderr | python -c "import sys, json; print(json.load(sys.stdin)['team_id'])")

# 3. read it back and confirm both windows persisted with reset_at
curl -s "http://localhost:4000/team/info?team_id=$TEAM_ID" \
  -H "Authorization: Bearer sk-1234" | python -m json.tool

Type

🐛 Bug Fix

Changes

new_team now serializes budget_limits with json.dumps on the create payload so the Json? column accepts the value, matching how the update and key paths already handle it. Added a regression test test_new_team_serializes_multi_window_budget_limits that builds a NewTeamRequest with two windows, runs new_team with admin auth, captures the data passed to litellm_teamtable.create, and asserts the stored budget_limits is a json string that decodes to a list carrying both windows plus a populated reset_at. The test fails before the fix (the value is a raw list) and passes after

@greptile-apps

greptile-apps Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a 500 error on POST /team/new when budget_limits is a multi-window list: Prisma's Json? column requires a JSON string, but jsonify_team_objectjsonify_object only stringifies dict values and passes lists through unencoded.

  • team_endpoints.py: adds json.dumps(budget_limits) immediately after model_dump, parallel to the existing router_settings serialization and the members_with_roles handling already in jsonify_team_object.
  • test_team_endpoints.py: adds test_new_team_serializes_multi_window_budget_limits, a fully-mocked regression test that verifies the stored value is a JSON string decoding to the expected list with populated reset_at fields.

Confidence Score: 5/5

The change is a one-liner serialization fix that aligns the create path with the update and key paths already doing the same thing; no new logic or control flow is introduced.

The fix is minimal and targeted: it serializes a list field to a JSON string before a Prisma write, exactly matching what the update path has done for the same column. The jsonify_object bypass for lists is confirmed in the source, _to_model correctly json-decodes strings on read, and the regression test covers the failure scenario end-to-end with proper mocking.

No files require special attention.

Important Files Changed

Filename Overview
litellm/proxy/management_endpoints/team_endpoints.py Adds json.dumps serialization for budget_limits before the Prisma create call, mirroring the existing router_settings and members_with_roles patterns; the placement (after model_dump, before jsonify_team_object) is correct and safe against double-encoding.
tests/test_litellm/proxy/management_endpoints/test_team_endpoints.py Adds a regression test that correctly mocks the DB layer (including overriding jsonify_team_object to identity), runs new_team, and asserts budget_limits is stored as a JSON string with both windows and populated reset_at fields; no real network calls made.

Reviews (1): Last reviewed commit: "fix(proxy): json-encode team budget_limi..." | Re-trigger Greptile

@codecov

codecov Bot commented Jun 22, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@mubashir1osmani

Copy link
Copy Markdown
Collaborator Author

Superseded by in-repo PR #31019 (pushed to BerriAI/litellm directly so full CI runs; cross-fork PRs skip the secret-gated jobs).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant