Skip to content

Fix KEY_BYTE indices parsing + guard optional legacy user fields - #432

Open
leslienwu wants to merge 4 commits into
d60:mainfrom
leslienwu:fix/ondemand-key-byte-indices-2026
Open

Fix KEY_BYTE indices parsing + guard optional legacy user fields#432
leslienwu wants to merge 4 commits into
d60:mainfrom
leslienwu:fix/ondemand-key-byte-indices-2026

Conversation

@leslienwu

@leslienwu leslienwu commented Jul 12, 2026

Copy link
Copy Markdown

Summary

  • Fixes Exception: Couldn't get KEY_BYTE indices, which currently breaks every request (see Title: ClientTransaction broken as of March 18 2026 — Couldn't get KEY_BYTE indices #408, Couldn't get KEY_BYTE indices #409). X changed the webpack chunk format used to reference ondemand.s.*.js in the homepage HTML, so ON_DEMAND_FILE_REGEX no longer matches.
    • Adapted from fix: update ondemand.s regex for new webpack chunk format #411 by @ryanstoic, which switches to a two-step lookup (find the chunk index for ondemand.s, then look up that chunk's hash separately).
    • Additionally hardened ON_DEMAND_HASH_PATTERN to accept both single- and double-quoted hash values — the original PR only handled double quotes, which left the same failure mode for single-quoted chunk data.
  • Fixes KeyError('urls') / KeyError('withheld_in_countries') raised by User.__init__ (both twikit/user.py and twikit/guest/user.py) when X omits entities.description.urls or withheld_in_countries from a user's legacy payload (observed for accounts with no links in their bio). Both fields now use .get(...) with safe defaults, matching the pattern already used for sibling optional fields like profile_banner_url.

Test plan

  • Verified locally with real, currently-valid Twitter cookies: Client.get_user_tweets() now succeeds end-to-end and returns real tweet content, where it previously raised Couldn't get KEY_BYTE indices immediately.
  • Verified the ON_DEMAND_HASH_PATTERN quote-handling fix with a standalone regex test against both single- and double-quoted synthetic chunk data.
  • Not tested against every account edge case (e.g. suspended/protected accounts) — only the two KeyError cases actually observed.

Summary by Sourcery

Update X client transaction parsing to restore KEY_BYTE index discovery and harden against changes in ondemand webpack chunks, while making user field handling resilient to missing legacy fields and documenting this fork as a temporary community patch.

Bug Fixes:

  • Restore KEY_BYTE index extraction by updating ondemand.s chunk detection and hash lookup to match X's current homepage bundle format.
  • Prevent KeyError when X omits user description URL entities or withheld_in_countries in both authenticated and guest user payloads by providing safe defaults.

Documentation:

  • Document this repository as an unofficial patch fork with installation instructions and a summary of the fixes included.

Summary by CodeRabbit

  • Bug Fixes
    • Improved compatibility with X’s current frontend by updating transaction processing and animation key index detection.
    • Prevented crashes when user profile legacy fields are missing by defaulting absent list values (e.g., description links, withheld countries, pinned tweet ids) to empty lists.
  • Documentation
    • Added an “IMPORTANT” callout describing an unofficial community patch fork and clarified its best-effort, personal-maintenance status.
    • Included direct install instructions for the patched version.

X changed how it references the ondemand.s.*.js bundle in the
homepage HTML, breaking ON_DEMAND_FILE_REGEX and causing
"Couldn't get KEY_BYTE indices" on every request (see d60#408,
d60#409).

Adapted from d60#411 by @ryanstoic, which switches to a
two-step lookup: find the webpack chunk index for "ondemand.s", then
look up that chunk's hash separately. Also hardened
ON_DEMAND_HASH_PATTERN to accept both single- and double-quoted hash
values (the original PR only handled double quotes, which left the
same failure mode for single-quoted chunk data).
X omits entities.description.urls and withheld_in_countries from the
legacy user payload for some accounts (observed on accounts with no
links in their bio). Direct dict indexing on these fields raised
KeyError('urls') / KeyError('withheld_in_countries') and aborted the
entire tweet/user fetch.

Switched both fields in User (twikit/user.py) and the guest-mode
User (twikit/guest/user.py) to .get(...) with safe defaults, matching
the pattern already used for sibling optional fields like
profile_banner_url and protected in the same classes.
Adds an unmissable banner at the top of the README so anyone landing
on this fork understands it's an unofficial compatibility patch, what
it fixes relative to upstream, and how to pip install it directly.
@sourcery-ai

sourcery-ai Bot commented Jul 12, 2026

Copy link
Copy Markdown

Reviewer's Guide

Updates the client transaction logic to match X’s new ondemand webpack chunk format and hardens user parsing against missing legacy fields, plus documents this fork as a temporary patch in the README.

Sequence diagram for updated get_indices ondemand chunk lookup

sequenceDiagram
    participant ClientTransaction
    participant Homepage as HomepageResponse
    participant Session
    participant AbsTwimg as AbsTwimgServer

    ClientTransaction->>ClientTransaction: get_indices(home_page_response, session, headers)
    ClientTransaction->>ClientTransaction: validate_response(home_page_response)
    ClientTransaction->>ClientTransaction: ON_DEMAND_FILE_REGEX.search(response)
    alt ondemand_chunk_found
        ClientTransaction->>ClientTransaction: re.search(ON_DEMAND_HASH_PATTERN.format(chunk_index), response)
        alt hash_found
            ClientTransaction->>AbsTwimg: session.request(GET, on_demand_file_url, headers)
            AbsTwimg-->>ClientTransaction: on_demand_file_response
            ClientTransaction->>ClientTransaction: INDICES_REGEX.finditer(on_demand_file_response.text)
            ClientTransaction->>ClientTransaction: key_byte_indices.append(group(1))
        else hash_not_found
            ClientTransaction->>ClientTransaction: [key_byte_indices remains empty]
        end
    else ondemand_chunk_not_found
        ClientTransaction->>ClientTransaction: [key_byte_indices remains empty]
    end
    ClientTransaction->>ClientTransaction: [if not key_byte_indices: raise Exception]
Loading

File-Level Changes

Change Details Files
Update ondemand webpack chunk discovery and hash lookup to match current X frontend bundle format.
  • Replace the old ON_DEMAND_FILE_REGEX with a pattern that captures the numeric chunk index for the ondemand.s entry in the homepage HTML.
  • Introduce ON_DEMAND_HASH_PATTERN to locate the hash for a specific chunk index, accepting both single- and double-quoted values.
  • Refactor get_indices to first find the ondemand.s chunk index, then resolve the corresponding hash and build the ondemand.s.a.js URL.
  • Simplify INDICES_REGEX to capture the numeric KEY_BYTE indices directly from the ondemand script contents and adjust extraction to use the new capture group.
twikit/x_client_transaction/transaction.py
Guard optional legacy user fields to avoid KeyError when X omits them.
  • Change description_urls to use entities.get('description', {}).get('urls', []) to safely handle missing description entities.
  • Change withheld_in_countries to use legacy.get('withheld_in_countries', []) to avoid KeyError for users without that field.
  • Apply these optional-field guards consistently to both authenticated and guest User classes.
twikit/user.py
twikit/guest/user.py
Document this repository as an unofficial patch fork and describe the fixes it provides and how to install it.
  • Add an IMPORTANT callout explaining that this is an unofficial community patch fork of d60/twikit.
  • Summarize the ondemand.s regex fix and optional user field handling in the README.
  • Provide a pip install command for installing this fork directly from Git via the specific branch.
  • Clarify that this is a short-term patch and recommend preferring upstream once fixes are merged.
README.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e4fd5a5a-7b9b-46c9-9869-403487cd333d

📥 Commits

Reviewing files that changed from the base of the PR and between 493e89e and 73e6d17.

📒 Files selected for processing (3)
  • README.md
  • twikit/guest/user.py
  • twikit/user.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • twikit/guest/user.py
  • twikit/user.py
  • README.md

📝 Walkthrough

Walkthrough

The patch updates X frontend transaction parsing, makes selected user fields tolerant of missing response data, and documents the unofficial fork and its installation command.

Changes

Frontend compatibility and user data handling

Layer / File(s) Summary
Updated transaction script extraction
twikit/x_client_transaction/transaction.py
Ondemand chunk and hash extraction use updated regexes, while fetched scripts are parsed with the revised indices pattern.
Defensive user field initialization
twikit/guest/user.py, twikit/user.py
description_urls, pinned_tweet_ids, and withheld_in_countries now default to empty lists when response fields are absent.
Patch fork installation documentation
README.md
Documents the unofficial fork, listed fixes, installation command, and maintenance status.

Estimated code review effort: 2 (Simple) | ~10 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ClientTransaction
  participant HomePage
  participant OndemandScript
  ClientTransaction->>HomePage: Fetch home page source
  ClientTransaction->>HomePage: Extract chunk id and file hash
  ClientTransaction->>OndemandScript: Fetch hashed ondemand script
  ClientTransaction->>OndemandScript: Extract animation indices
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main fixes: KEY_BYTE index parsing updates and safer handling of optional legacy user fields.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • In get_indices, consider operating directly on home_page_response.text instead of str(response) to avoid relying on the string representation of the response object and make the intent of the regex parsing clearer.
  • The new ON_DEMAND_FILE_REGEX and ON_DEMAND_HASH_PATTERN are tightly coupled to a specific bundle layout; adding a short comment describing the expected HTML/JSON snippet format would make future maintenance and adjustments to X’s changes much easier.
  • The logic for optional user fields (description_urls, withheld_in_countries) is duplicated between user.py and guest/user.py; consider extracting a small helper or shared initializer to keep this behavior consistent and reduce the risk of them drifting apart.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `get_indices`, consider operating directly on `home_page_response.text` instead of `str(response)` to avoid relying on the string representation of the response object and make the intent of the regex parsing clearer.
- The new `ON_DEMAND_FILE_REGEX` and `ON_DEMAND_HASH_PATTERN` are tightly coupled to a specific bundle layout; adding a short comment describing the expected HTML/JSON snippet format would make future maintenance and adjustments to X’s changes much easier.
- The logic for optional user fields (`description_urls`, `withheld_in_countries`) is duplicated between `user.py` and `guest/user.py`; consider extracting a small helper or shared initializer to keep this behavior consistent and reduce the risk of them drifting apart.

## Individual Comments

### Comment 1
<location path="twikit/guest/user.py" line_range="96-97" />
<code_context>
         self.location: str = legacy['location']
         self.description: str = legacy['description']
-        self.description_urls: list = legacy['entities']['description']['urls']
+        self.description_urls: list = legacy['entities'].get('description', {}).get('urls', [])
         self.urls: list = legacy['entities'].get('url', {}).get('urls')
         self.pinned_tweet_ids: list[str] = legacy['pinned_tweet_ids_str']
         self.is_blue_verified: bool = data['is_blue_verified']
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Use a consistent default for `urls` to avoid `None` vs `[]` inconsistencies.

`description_urls` now defaults to `[]`, but `urls` still becomes `None` when `legacy['entities']['url']` is missing. This mismatch can break code that assumes both are lists. Please also default `urls` to an empty list, e.g.:

```python
self.urls: list = legacy['entities'].get('url', {}).get('urls', [])
```

The same adjustment likely applies in the `User` class to keep behavior consistent there as well.

Suggested implementation:

```python
        self.description_urls: list = legacy['entities'].get('description', {}).get('urls', [])
        self.urls: list = legacy['entities'].get('url', {}).get('urls', [])

```

To fully apply your suggestion, wherever the main `User` class (non-guest) assigns `self.urls` from `legacy['entities']['url']['urls']`, adjust it to:

```python
self.urls: list = legacy['entities'].get('url', {}).get('urls', [])
```

This ensures both guest users and regular users consistently default `urls` to `[]` instead of `None`, avoiding list vs `None` inconsistencies in downstream code.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread twikit/guest/user.py
Comment on lines +96 to 97
self.description_urls: list = legacy['entities'].get('description', {}).get('urls', [])
self.urls: list = legacy['entities'].get('url', {}).get('urls')

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion (bug_risk): Use a consistent default for urls to avoid None vs [] inconsistencies.

description_urls now defaults to [], but urls still becomes None when legacy['entities']['url'] is missing. This mismatch can break code that assumes both are lists. Please also default urls to an empty list, e.g.:

self.urls: list = legacy['entities'].get('url', {}).get('urls', [])

The same adjustment likely applies in the User class to keep behavior consistent there as well.

Suggested implementation:

        self.description_urls: list = legacy['entities'].get('description', {}).get('urls', [])
        self.urls: list = legacy['entities'].get('url', {}).get('urls', [])

To fully apply your suggestion, wherever the main User class (non-guest) assigns self.urls from legacy['entities']['url']['urls'], adjust it to:

self.urls: list = legacy['entities'].get('url', {}).get('urls', [])

This ensures both guest users and regular users consistently default urls to [] instead of None, avoiding list vs None inconsistencies in downstream code.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@README.md`:
- Around line 1-28: Update the README banner’s install command fence to specify
the bash language, and remove the trailing empty blockquote marker after the
closing fence so the blockquote has no blank line. Preserve the existing command
and surrounding text unchanged.

In `@twikit/guest/user.py`:
- Line 96: Update the description_urls initialization in the User construction
flow to handle legacy['entities'] being None before calling get. Preserve the
existing empty-list fallback when the entities or description data is absent,
while retaining URL extraction for populated entities.
- Line 115: Update the withheld_in_countries initialization in the user data
construction flow to normalize both missing and None values to an empty list,
while preserving any provided country list. Use the existing legacy value
retrieval rather than introducing additional state.

In `@twikit/user.py`:
- Line 124: Update the `withheld_in_countries` assignment in the user model to
use the legacy value when present but fall back to an empty list when it is
`None`, matching the handling in `twikit/guest/user.py`.
- Line 102: Update the user initialization logic around description_urls to
handle legacy['entities'] being None before calling .get(), matching the
defensive handling already implemented in twikit/guest/user.py. Preserve the
existing empty URL-list fallback when entities or description data is absent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4ab6c920-d511-40f0-9921-7453b9ec95d6

📥 Commits

Reviewing files that changed from the base of the PR and between c3b7220 and 493e89e.

📒 Files selected for processing (4)
  • README.md
  • twikit/guest/user.py
  • twikit/user.py
  • twikit/x_client_transaction/transaction.py

Comment thread README.md
Comment thread twikit/guest/user.py
self.location: str = legacy['location']
self.description: str = legacy['description']
self.description_urls: list = legacy['entities']['description']['urls']
self.description_urls: list = legacy['entities'].get('description', {}).get('urls', [])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

legacy['entities'] can be None, causing AttributeError.

The upstream build_user_data (in twikit/utils.py) constructs legacy with 'entities': raw_data.get('entities'), so the entities key is always present but its value can be None. When that happens, legacy['entities'].get(...) raises AttributeError: 'NoneType' object has no attribute 'get' — the very crash this PR intends to prevent.

🛡️ Proposed fix: use safe access for the entities key
-        self.description_urls: list = legacy['entities'].get('description', {}).get('urls', [])
+        self.description_urls: list = (legacy.get('entities') or {}).get('description', {}).get('urls', [])
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
self.description_urls: list = legacy['entities'].get('description', {}).get('urls', [])
self.description_urls: list = (legacy.get('entities') or {}).get('description', {}).get('urls', [])
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@twikit/guest/user.py` at line 96, Update the description_urls initialization
in the User construction flow to handle legacy['entities'] being None before
calling get. Preserve the existing empty-list fallback when the entities or
description data is absent, while retaining URL extraction for populated
entities.

Comment thread twikit/guest/user.py
self.is_translator: bool = legacy['is_translator']
self.translator_type: str = legacy['translator_type']
self.withheld_in_countries: list[str] = legacy['withheld_in_countries']
self.withheld_in_countries: list[str] = legacy.get('withheld_in_countries', [])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

legacy.get('withheld_in_countries', []) returns None when the key exists but the value is None.

build_user_data always sets 'withheld_in_countries': raw_data.get('withheld_in_countries'), so the key is present but the value can be None. dict.get(key, default) only returns the default when the key is missing, not when the value is None. Consider (legacy.get('withheld_in_countries') or []) to also handle None values.

🛡️ Proposed fix
-        self.withheld_in_countries: list[str] = legacy.get('withheld_in_countries', [])
+        self.withheld_in_countries: list[str] = legacy.get('withheld_in_countries') or []
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
self.withheld_in_countries: list[str] = legacy.get('withheld_in_countries', [])
self.withheld_in_countries: list[str] = legacy.get('withheld_in_countries') or []
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@twikit/guest/user.py` at line 115, Update the withheld_in_countries
initialization in the user data construction flow to normalize both missing and
None values to an empty list, while preserving any provided country list. Use
the existing legacy value retrieval rather than introducing additional state.

Comment thread twikit/user.py
self.location: str = legacy['location']
self.description: str = legacy['description']
self.description_urls: list = legacy['entities']['description']['urls']
self.description_urls: list = legacy['entities'].get('description', {}).get('urls', [])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Same legacy['entities'] None issue as twikit/guest/user.py.

legacy['entities'] can be None (upstream build_user_data uses raw_data.get('entities')), causing AttributeError on .get(...). This is the same incomplete defensive coding as in twikit/guest/user.py line 96.

🛡️ Proposed fix
-        self.description_urls: list = legacy['entities'].get('description', {}).get('urls', [])
+        self.description_urls: list = (legacy.get('entities') or {}).get('description', {}).get('urls', [])
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
self.description_urls: list = legacy['entities'].get('description', {}).get('urls', [])
self.description_urls: list = (legacy.get('entities') or {}).get('description', {}).get('urls', [])
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@twikit/user.py` at line 102, Update the user initialization logic around
description_urls to handle legacy['entities'] being None before calling .get(),
matching the defensive handling already implemented in twikit/guest/user.py.
Preserve the existing empty URL-list fallback when entities or description data
is absent.

Comment thread twikit/user.py
self.is_translator: bool = legacy['is_translator']
self.translator_type: str = legacy['translator_type']
self.withheld_in_countries: list[str] = legacy['withheld_in_countries']
self.withheld_in_countries: list[str] = legacy.get('withheld_in_countries', [])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Same withheld_in_countries None issue as twikit/guest/user.py.

legacy.get('withheld_in_countries', []) returns None (not []) when the key exists with a None value. Use or [] to handle both cases.

🛡️ Proposed fix
-        self.withheld_in_countries: list[str] = legacy.get('withheld_in_countries', [])
+        self.withheld_in_countries: list[str] = legacy.get('withheld_in_countries') or []
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
self.withheld_in_countries: list[str] = legacy.get('withheld_in_countries', [])
self.withheld_in_countries: list[str] = legacy.get('withheld_in_countries') or []
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@twikit/user.py` at line 124, Update the `withheld_in_countries` assignment in
the user model to use the legacy value when present but fall back to an empty
list when it is `None`, matching the handling in `twikit/guest/user.py`.

legacy['pinned_tweet_ids_str'] is absent for accounts that have never
pinned a tweet (observed on several corporate/official accounts,
e.g. accounts used purely for announcements), raising
KeyError('pinned_tweet_ids_str') and aborting the whole tweet fetch.

Same fix as the description_urls / withheld_in_countries guards
already applied here: switched to .get(...) with an empty-list
default in both User (twikit/user.py) and the guest-mode User
(twikit/guest/user.py).
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