Skip to content

Conversation

JVenberg
Copy link
Contributor

@JVenberg JVenberg commented Oct 2, 2025

Title

Fix: Separate key expiration from budget reset logic

Relevant issues

Fixes key expiration bug where API keys expire at standardized intervals instead of actual duration from creation time.

Fixes: #15142

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • I have Added testing in the tests/litellm/ directory, Adding at least 1 test is a hard requirement - see details

    • Added 3 new unit tests in tests/test_litellm/proxy/management_endpoints/test_key_management_endpoints.py
    • Updated 1 existing test to remove incorrect assertions
  • I have added a screenshot of my new test passing locally

    ✅ test_key_expiration_calculated_from_current_time PASSED
    ✅ test_key_expiration_with_various_durations PASSED
    ✅ test_key_budget_reset_uses_standardized_time PASSED
    ✅ test_budget_reset_and_expires_at_first_of_month PASSED
    
    4 passed in 2.19s
    
  • 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

    • Changes only affect key expiration calculation (4 lines in production code)

Type

🐛 Bug Fix

Changes

Problem

Key expiration was incorrectly using budget reset logic (get_budget_reset_time()), causing keys to expire at standardized intervals (e.g., 1st of next month) instead of the actual duration from creation time.

Example of the bug:

  • Create key on October 15th with duration="1mo"
  • Before: Key expires November 1st (only 16 days)
  • After: Key expires November 15th (full 30 days)

Why This Is Wrong

Key expiration and budget reset serve fundamentally different purposes and must use different calculation methods:

1. Budget Reset (Financial/Accounting)

  • Purpose: Track spending in predictable billing cycles
  • Behavior: Must reset at standardized intervals (e.g., 1st of month at midnight)
  • Reason: Makes financial reporting, budgeting, and spend tracking consistent and predictable across all users
  • Correct Implementation: Uses get_budget_reset_time() for standardized intervals

2. Key Expiration (Security/Access Control)

  • Purpose: Limit the lifetime of API keys for security
  • Behavior: Must expire relative to creation time
  • Reason:
    • Users expect "1 month duration" to mean 30 days from now, not "until end of current month"
    • Keys created late in month would get dramatically shorter lifetimes (e.g., only 1 day if created on Jan 31)
    • Security policies requiring specific key lifetimes (e.g., "keys must rotate every 90 days") cannot be reliably enforced
    • Key rotation planning becomes impossible with unpredictable expiration times
  • Correct Implementation: Uses duration_in_seconds() with relative time calculation

Solution

File: litellm/proxy/management_endpoints/key_management_endpoints.py (lines 1678-1682)

Changed key expiration calculation from:

# Before (buggy) - Used budget reset logic
if duration is None:
    expires = None
else:
    expires = get_budget_reset_time(budget_duration=duration)  # Wrong

To:

# After (fixed) - Uses proper duration calculation
if duration is None:
    expires = None
else:
    duration_s = duration_in_seconds(duration=duration)
    expires = datetime.now(timezone.utc) + timedelta(seconds=duration_s)  # Correct

Note: Budget reset logic remains unchanged and continues to use get_budget_reset_time() for standardized intervals.

Testing

New Tests Added

  1. test_key_expiration_calculated_from_current_time

    • Verifies expires is calculated as current_time + duration (not standardized)
    • Verifies budget_reset_at is standardized to 1st of next month
    • Confirms they have different values (proving the fix works)
  2. test_key_expiration_with_various_durations

    • Tests multiple duration units: 30s, 5m, 2h, 7d
    • Ensures all duration types calculate correctly
  3. test_key_budget_reset_uses_standardized_time

    • Confirms budget resets still use standardized intervals (not affected by fix)

Updated Test

  • test_budget_reset_and_expires_at_first_of_month
    • Removed incorrect assertions expecting expires to be on 1st of month
    • Now only tests budget reset behavior (which should be standardized)

Test Results

$ poetry run pytest tests/test_litellm/proxy/management_endpoints/test_key_management_endpoints.py::test_key_expiration_calculated_from_current_time tests/test_litellm/proxy/management_endpoints/test_key_management_endpoints.py::test_key_expiration_with_various_durations tests/test_litellm/proxy/management_endpoints/test_key_management_endpoints.py::test_key_budget_reset_uses_standardized_time tests/test_litellm/proxy/management_endpoints/test_key_management_endpoints.py::test_budget_reset_and_expires_at_first_of_month -v

✅ test_budget_reset_and_expires_at_first_of_month PASSED
✅ test_key_budget_reset_uses_standardized_time PASSED
✅ test_key_expiration_calculated_from_current_time PASSED
✅ test_key_expiration_with_various_durations PASSED

4 passed in 2.19s

Files Changed

  1. litellm/proxy/management_endpoints/key_management_endpoints.py

    • Fixed key expiration calculation (4 lines changed)
    • Budget reset logic unchanged
  2. tests/test_litellm/proxy/management_endpoints/test_key_management_endpoints.py

    • Added 3 new comprehensive tests
    • Updated 1 existing test to remove incorrect assertions

Backward Compatibility

This change only affects NEW keys created after the fix. Existing keys with already-set expiration times are not modified.

The fix changes how expiration is calculated for new keys:

  • Existing keys: Retain their current expiration dates (no changes to existing database records)
  • New keys: Will have correctly calculated expiration dates based on creation time + duration

While this may affect users relying on the buggy behavior for new key creation:

  • The previous behavior was incorrect (keys could expire in less than 16 days for "1mo" duration)
  • Budget reset functionality is unchanged
  • All existing tests pass with the fix
  • This aligns with user expectations and standard duration semantics
  • Security policies can now be enforced consistently

Copy link

vercel bot commented Oct 2, 2025

@JVenberg is attempting to deploy a commit to the CLERKIEAI Team on Vercel.

A member of the Team first needs to authorize it.

@CLAassistant
Copy link

CLAassistant commented Oct 2, 2025

CLA assistant check
All committers have signed the CLA.

@JVenberg JVenberg changed the title Fix: Separate key expiration from budget reset logic [Fix] Separate key expiration from budget reset logic Oct 2, 2025
@krrishdholakia
Copy link
Contributor

@emerzon would you consider this a bug or expected behaviour?

asking us i recall a lot of feedback around reset budgets needing to be at month start instead of 1 month from budget creation, wondering if something similar would apply here

@emerzon
Copy link
Contributor

emerzon commented Oct 3, 2025

@krrishdholakia I can say this can be a bit confusing. maybe would make sense to have specific periods for relative times, like next_week, next_month or something similar.. and use the same for both key expiration and budget reset

@JVenberg
Copy link
Contributor Author

JVenberg commented Oct 4, 2025

Thanks for the feedback, @krrishdholakia and @emerzon

My understanding was that key expiration (for security) should be relative to the creation date, while budget resets (for finance) should be standardized to the calendar month

Could you help me understand the use case for standardizing the key expiration as well? I'm trying to understand the scenario where a key with a "1 month" duration should expire in just a day if it's created at the end of a month

@famished-programmer
Copy link

I would concur with the current implementation not working as my team expected. I would not expect an virtual key set with an expiration date 30days in the future to expire 1 day from now. Which is currently happening in our environment.

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.

[Bug]: Key expiration uses budget reset logic instead of duration calculation

5 participants