iflytek/astron-agent · error · TypeError

RedisCache only accepts values that can be pickled.

Error message

RedisCache only accepts values that can be pickled. 

What it means

hash_get() fetches a hash field with HGET, then calls result.decode('utf-8') and json.loads(). A TypeError is raised when result is None (field missing) or already a str (decode_mode not set to bytes), and the handler re-raises a misleading TypeError claiming the value 'cannot be pickled'. Despite the message, the actual cause is a bytes-decoding or JSON-deserialization type error.

Solutions

  1. Check the field exists first: if self._client.hexists(name, key) is False, treat as cache miss and return None instead of decoding.
  2. Guard the result: if result is None: return None before calling result.decode().
  3. Ensure the client does NOT use decode_responses=True (or handle str: skip .decode() when isinstance(result, str)).
  4. Ignore the misleading message — the chained exception (`from exc`) shows the true TypeError at the decode/json line; fix that condition.

Example fix

# before
result = self._client.hget(name=name, key=key)
result_str = result.decode("utf-8")
return json.loads(result_str)
# after
result = self._client.hget(name=name, key=key)
if result is None:
    return None
raw = result.decode("utf-8") if isinstance(result, bytes) else result
return json.loads(raw)
Defensive patterns

Strategy: type-guard

Validate before calling

def hash_field_exists(svc, name: str, key: str) -> bool:
    return bool(svc._client.hexists(name, key))

Type guard

def hash_result_ok(result) -> bool:
    return result is not None and (isinstance(result, (bytes, str)))

Try / catch

try:
    value = redis_service.hash_get(name, key)
except TypeError:
    value = None  # treat as cache miss; the message about pickling is misleading

Prevention

When it happens

Trigger: HGET returns None because the hash name or field key does not exist, so result.decode raises TypeError: 'NoneType' object has no attribute 'decode'; the Redis client was created with decode_responses=True so result is already str and .decode() fails; a stored value is not valid for the decode/json path.

Common situations: Reading a cache key that was never written or has expired; constructing redis.Redis(..., decode_responses=True) elsewhere in config while hash_get assumes bytes; wrong hash name/key casing; relying on the misleading 'pickle' message and debugging the wrong direction (serialization vs missing key).

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of iflytek/astron-agent@5e758547a8 (2026-09-12). Data as JSON: /api/errors/5b8ac66c283f0caa. Report an issue: GitHub.

Appendix: source

Thrown at core/plugin/link/domain/models/utils.py:337

        """Get a field value from a Redis hash.

        Args:
            name: The hash name.
            key: The field key within the hash.

        Returns:
            Any: The deserialized value from the hash field.

        Raises:
            TypeError: If the value cannot be deserialized.
        """
        try:
            result = self._client.hget(name=name, key=key)  # type: ignore[union-attr]
            print("result: ", result)
            result_str = result.decode("utf-8")  # type: ignore[union-attr]
            return json.loads(result_str)
        except TypeError as exc:
            raise TypeError(
                "RedisCache only accepts values that can be pickled. "
            ) from exc

    def hash_del(self, name: str, *key: str) -> Tuple[bool, Dict[str, str]]:
        """Delete one or more fields from a Redis hash.

        Args:
            name: The hash name.
            *key: Variable number of field keys to delete.

        Returns:
            tuple: (success_boolean, dict_of_failed_deletions)

        Raises:
            TypeError: If the operation fails due to type issues.
        """
        try:
            result = self._client.hdel(name, *key)

View on GitHub (pinned to 5e758547a8)