Fix 'float' Object Is Not Subscriptable in API Responses

PYTHON Published Updated 3 mins read By Leon Wei
Fix 'float' Object Is Not Subscriptable in API Responses cover image

Quick summary

Summarize this blog with AI

If payload['scores'][0] raises TypeError: 'float' object is not subscriptable, the immediate operation is wrong because payload['scores'] is one number. In an API or dictionary pipeline, the deeper question is why a field expected to contain a list became a scalar.

This guide follows that upstream contract problem. For a quick explanation of indexing a scalar, use the general float-indexing guide. The examples here are synthetic in-memory payloads, tested with Python 3.12.14; they make no network requests.

A dictionary default does not change an existing value

payload = {'scores': 9.7}
scores = payload.get('scores', [])
print(type(scores).__name__)
try:
    print(scores[0])
except TypeError as exc:
    print(str(exc))

Expected output:

float
'float' object is not subscriptable

The empty-list default applies only if the key is absent. It does not wrap the existing float in a list or validate its type. Replacing the failure with str(scores)[0] changes the task to reading a character. Wrapping every value in a list can hide a schema change instead of correcting it.

Inspect the value at each boundary

import json

payload = json.loads('{"result": {"scores": 9.7}}')
result = payload['result']
print(type(payload).__name__)
print(type(result).__name__)
print(type(result['scores']).__name__)

Expected output:

dict
dict
float

The JSON parser has correctly decoded an object containing a numeric scalar. A parser cannot infer that the producer intended an array. Compare the actual field shape with the API specification or producer's return contract. In production diagnostics, log field paths and types rather than complete private payloads.

Validate a list-of-scores contract explicitly

This example requires a result object with a nonempty scores list. Each score must be a finite built-in integer or float between 0 and 100. Booleans are rejected despite being integer subclasses. The validator returns a fresh list, so the consumer cannot mutate the producer's list by accident.

import math

def read_scores(payload):
    if not isinstance(payload, dict):
        raise TypeError('payload must be an object')
    result = payload.get('result')
    if not isinstance(result, dict):
        raise TypeError('result must be an object')
    if 'scores' not in result:
        raise ValueError('result.scores is required')
    scores = result['scores']
    if not isinstance(scores, list):
        raise TypeError('result.scores must be a list')
    if not scores:
        raise ValueError('result.scores must not be empty')
    for index, score in enumerate(scores):
        if type(score) not in (int, float):
            raise TypeError(f'result.scores[{index}] must be a number')
        if not 0 <= score <= 100 or not math.isfinite(score):
            raise ValueError(f'result.scores[{index}] must be finite and in range')
    return scores.copy()

print(read_scores({'result': {'scores': [9.7, 10]}}))
for payload in (
    {'result': {'scores': 9.7}},
    {'result': {}},
    {'result': {'scores': []}},
    {'result': {'scores': [True]}},
):
    try:
        read_scores(payload)
    except (TypeError, ValueError) as exc:
        print(type(exc).__name__ + ': ' + str(exc))

Expected output:

[9.7, 10]
TypeError: result.scores must be a list
ValueError: result.scores is required
ValueError: result.scores must not be empty
TypeError: result.scores[0] must be a number

The bounds check also rejects huge integers before a float-range conversion is attempted. If the real API permits scalar scores, optional values, or an empty list, define those cases explicitly instead of copying this teaching policy unchanged.

Keep function return shapes consistent

def summarize_scores(scores):
    if not scores:
        raise ValueError('at least one score is required')
    return {'count': len(scores), 'mean': sum(scores) / len(scores)}

scores = read_scores({'result': {'scores': [10, 20]}})
summary = summarize_scores(scores)
print(summary)
print(summary['mean'])

Expected output:

{'count': 2, 'mean': 15.0}
15.0

This summary always returns a dictionary on success; its mean field is always a scalar. Returning a float on one branch and a record on another makes the caller's indexing assumption fragile. Type annotations and static checks help document the expected shape, but runtime data from a remote boundary still needs validation.

Test the producer and consumer together

Include a valid list, a scalar replacing the list, a missing field, an empty list, null, a boolean, and an out-of-range value. When a failure occurs, keep the traceback and identify the earliest boundary where the contract changes. Repair that boundary or update the consumer deliberately; do not catch TypeError and substitute a fabricated zero score.

This URL remains available for the API-contract problem. For DataFrame aggregation that intentionally returns a scalar, use the pandas scalar-shape guide. Primary references: JSON decoding types and dictionary get.

Interview Prep

Begin Your SQL, Python, and R Journey

Master 230 interview-style coding questions and build the data skills needed for analyst, scientist, and engineering roles.

Related Articles

All Articles