Skip to content

Conversation

@springfall2008
Copy link
Owner

Summary

Fixes Octopus API rate limiting error handling (KT-CT-1199) and implements automatic token refresh with retry for authentication errors (KT-CT-1139/1111/1143).

Changes

Core Functionality

  • Rate limit detection: async_read_response now detects rate limit errors (KT-CT-1199) early and returns None immediately without retry
  • Auth error retry: async_graphql_query detects auth errors (KT-CT-1139/1111/1143), forces token refresh, and retries once
  • Data preservation: Existing data is never overridden when API calls fail due to rate limiting
  • Error handling: async_read_response returns full response data (except rate limits) to allow caller to handle errors appropriately

Implementation Details

  • Modified async_read_response() in octopus.py:
    • Check for rate limit error code KT-CT-1199 and return None immediately
    • Return full response for other errors to allow caller to handle them
  • Modified async_graphql_query() in octopus.py:
    • Check response for auth error codes after API call
    • Clear token and call async_refresh_token() on auth error
    • Recursively retry query once with _retry_count=1 to prevent infinite loops
    • Log and handle other GraphQL errors appropriately
  • Removed unused in_refresh parameter from async_refresh_token()

Testing

  • Added comprehensive test suite: test_octopus_rate_limit.py (11 test scenarios):
    • Rate limit error detection and handling
    • Data preservation during failures
    • Token refresh failure handling
    • Automatic token refresh on auth errors
    • Multiple auth error codes (KT-CT-1139/1111/1143)
  • Updated test_octopus_read_response.py: Adjusted expectations to match new behavior where full response is returned
  • Fixed test_fetch_octopus_rates.py: Clear dummy_items for test isolation
  • Fixed test cleanup: Restore mocked get_state_wrapper to prevent interference between tests

Test Results

✅ All 13 octopus tests pass:

  • octopus_url ✅
  • octopus_cache ✅
  • octopus_events ✅
  • octopus_refresh_token ✅
  • octopus_read_response ✅
  • octopus_rate_limit ✅ (new)
  • octopus_fetch_previous_dispatch ✅
  • fetch_octopus_rates ✅
  • add_now_to_octopus_slot ✅
  • octopus_slots ✅
  • octopus_free ✅
  • ohme_run_octopus ✅
  • download_octopus_rates ✅

Benefits

  1. Prevents data corruption: Rate limit errors no longer override existing cached data
  2. Improved reliability: Automatic token refresh and retry for expired tokens
  3. Better error handling: Clear separation between rate limits (no retry) and auth errors (retry)
  4. Comprehensive test coverage: All error scenarios are tested and validated

- Add rate limit error detection (KT-CT-1199) with immediate return
- Implement automatic token refresh and retry for auth errors (KT-CT-1139/1111/1143)
- Preserve existing data when API calls fail due to rate limiting
- Return full response from async_read_response except for rate limits
- Handle auth errors in async_graphql_query with single retry after token refresh
- Add comprehensive test suite (test_octopus_rate_limit.py) with 11 test scenarios
- Update test_octopus_read_response.py to match new error handling behavior
- Fix test cleanup: restore mocked functions to prevent test interference
- Clear dummy_items in fetch_octopus_rates test for test isolation

All octopus tests now pass (13/13).
Copilot AI review requested due to automatic review settings December 22, 2025 10:42
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements robust error handling for Octopus Energy API rate limiting and authentication errors. It addresses rate limit errors (KT-CT-1199) by immediately returning None without retry, while implementing automatic token refresh with one retry for authentication errors (KT-CT-1139/1111/1143). The implementation ensures existing cached data is preserved when API calls fail due to rate limiting.

Key changes:

  • Rate limit errors are detected early in async_read_response and return None immediately to prevent data corruption
  • Auth errors trigger automatic token refresh and single retry in async_graphql_query with infinite loop prevention
  • Comprehensive test suite validates all error scenarios including data preservation, token refresh failures, and multi-error handling

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
apps/predbat/octopus.py Core implementation: rate limit detection in async_read_response, auth error retry logic in async_graphql_query, and improved error handling with data preservation
apps/predbat/tests/test_octopus_rate_limit.py New comprehensive test suite covering rate limiting, token refresh, auth errors, and data preservation scenarios across 11 test cases
apps/predbat/tests/test_octopus_read_response.py Updated test expectations to reflect new behavior where full response data is returned for non-rate-limit errors
apps/predbat/tests/test_fetch_octopus_rates.py Test isolation fix: clears dummy_items to prevent interference between tests
apps/predbat/unit_test.py Integration of new test suite into test runner
apps/predbat/predbat.py Version bump to v8.30.4

- Test 5: Rate limit followed by successful request
- Test 6: Rate limit error on async_get_account - account_data preserved
- Test 7: async_graphql_query fails immediately when token refresh fails
- Test 8: Token refresh fails during retry after auth error
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test 8 is documented in the docstring but is not implemented in the test function. Either implement the test case "Token refresh fails during retry after auth error" or remove it from the documentation to ensure the test suite accurately reflects what is being tested.

Copilot uses AI. Check for mistakes.
Comment on lines +430 to +431
print("PASS: ignore_errors=True suppresses failure counting when token refresh fails")

Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment says "failures_total should increment even with ignore errors (due to API key)" but this is misleading. The failures_total increments on token refresh failure regardless of the ignore_errors parameter. The increment occurs at line 1234 of octopus.py before any ignore_errors check. Consider clarifying the comment to accurately reflect that token refresh failures always increment failures_total, independent of ignore_errors.

Copilot uses AI. Check for mistakes.

# Test 9: Expired token is automatically refreshed and query retried successfully
print("\n*** Test 9: Expired token is automatically refreshed and query retried successfully ***")
api = OctopusAPI(my_predbat, key="test-key", account_id="test-account", automatic=False)
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test 7c's print statement says "ignore_errors=True suppresses failure counting when token refresh fails" but this is inaccurate - the test actually verifies that failures_total DOES increment (line 430 expects initial_failures + 1). The print message contradicts the test expectation. Consider changing the message to something like "failures_total increments on token refresh failure regardless of ignore_errors".

Copilot uses AI. Check for mistakes.
}

# Call update - should not override existing data when API fails
result = await api.async_update_intelligent_device("test-account")
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assignment to 'result' is unnecessary as it is redefined before this value is used.

Suggested change
result = await api.async_update_intelligent_device("test-account")
await api.async_update_intelligent_device("test-account")

Copilot uses AI. Check for mistakes.

if api.failures_total != initial_failures + 1:
print(f"ERROR: failures_total not incremented. Expected {initial_failures + 1}, got {api.failures_total}")
failed = True
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assignment to 'result' is unnecessary as it is redefined before this value is used.

Copilot uses AI. Check for mistakes.

if api.failures_total != initial_failures + 1:
print(f"ERROR: failures_total should increment even with ignore errors (due to API key) Expected {initial_failures + 1}, got {api.failures_total}")
failed = True
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assignment to 'result' is unnecessary as it is redefined before this value is used.

Copilot uses AI. Check for mistakes.
import asyncio
import json
from unittest.mock import AsyncMock, MagicMock
from octopus import OctopusAPI
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Import of 'integration_context_header' is not used.

Copilot uses AI. Check for mistakes.
@springfall2008 springfall2008 merged commit daacaa8 into main Dec 22, 2025
1 check passed
@springfall2008 springfall2008 deleted the fixes22 branch December 22, 2025 10:53
iangregory pushed a commit to iangregory/batpred that referenced this pull request Dec 24, 2025
…ll2008#3105)

* Fix Octopus API rate limiting and auth error retry handling

- Add rate limit error detection (KT-CT-1199) with immediate return
- Implement automatic token refresh and retry for auth errors (KT-CT-1139/1111/1143)
- Preserve existing data when API calls fail due to rate limiting
- Return full response from async_read_response except for rate limits
- Handle auth errors in async_graphql_query with single retry after token refresh
- Add comprehensive test suite (test_octopus_rate_limit.py) with 11 test scenarios
- Update test_octopus_read_response.py to match new error handling behavior
- Fix test cleanup: restore mocked functions to prevent test interference
- Clear dummy_items in fetch_octopus_rates test for test isolation

All octopus tests now pass (13/13).

* [pre-commit.ci lite] apply automatic fixes

---------

Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
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.

2 participants