hashgraph / hashgraph/hedera-agent-kit-py
Bug: normalise_delete_non_fungible_token_allowance silently drops scheduling_params and skips normalisation
- Dominant language
- Python
- Stars
- 11
- Forks
- 10
- PR merge metrics
- No merged PRs in 30d
Description
## Summary
`HederaParameterNormaliser.normalise_delete_non_fungible_token_allowance` contains two related
correctness bugs on the same line that cause scheduling parameters to be silently lost when a
caller attempts to schedule an NFT allowance deletion. A third related issue exists at the
call site in `DeleteNonFungibleTokenAllowanceTool`.
## Location
- `python/hedera_agent_kit/shared/hedera_utils/hedera_parameter_normalizer.py`, line 2410
- `python/hedera_agent_kit/plugins/core_token_plugin/delete_non_fungible_token_allowance.py`, line 97
## The Bugs
### Bug 1 — wrong source variable (line 2410)
The function parses and validates raw input into `parsed_params` at lines 2381–2386. Every
other field in the return statement correctly reads from `parsed_params`. Line 2410 alone reads
from `params` — the raw, unvalidated input.
\`\`\`python
# current (wrong)
scheduling_params=getattr(params, "scheduling_params", None),
# all other fields in the same return statement read from parsed_params
transaction_memo=parsed_params.transaction_memo,
\`\`\`
When `params` arrives as a plain `dict` (the standard path in LLM agent contexts),
`getattr(dict, "scheduling_params", None)` returns `None` — dicts do not support attribute
access. The scheduling intent is silently discarded with no error or warning.
### Bug 2 — normalisation step is skipped entirely (line 2410)
Every other scheduling-capable normaliser in this file (11+ methods) follows the same pattern:
\`\`\`python
scheduling_params = None
if getattr(parsed_params, "scheduling_params", None):
if parsed_params.scheduling_params.is_scheduled:
scheduling_params = await HederaParameterNormaliser.normalise_scheduled_transaction_params(
parsed_params.scheduling_params, context, client
)
\`\`\`
This converts the raw `OptionalScheduledTransactionParams` into the `ScheduleCreateParams`
type expected by `tx_mode_strategy` downstream. `normalise_delete_non_fungible_token_allowance`
skips this call and passes the raw object directly. Even when `params` is already a Pydantic
model and Bug 1 does not trigger, the un-normalised value causes a type mismatch at execution
time.
### Bug 3 — missing `await` at call site (line 97)
`DeleteNonFungibleTokenAllowanceTool.normalize_params` calls the normaliser without `await`:
\`\`\`python
# delete_non_fungible_token_allowance.py line 97
async def normalize_params(self, params, context, client):
return HederaParameterNormaliser.normalise_delete_non_fungible_token_allowance(
params, context, client
)
\`\`\`
The normaliser is currently a synchronous `@staticmethod`, so this works today. However, the
correct fix for Bug 2 requires making the normaliser `async` (to `await` the
`normalise_scheduled_transaction_params` call). Without also adding `await` here, that fix
would silently return a coroutine object instead of the normalised params, breaking all
non-scheduled calls as well.
## Impact
Any attempt to schedule an NFT allowance deletion via this tool will either:
- Silently execute as an **immediate** transaction (scheduling dropped, no error raised), or
- Fail at execution time with a type mismatch in the transaction mode strategy
The failure is silent in the most common case — the tool returns a transaction ID for an
immediate execution while the caller expected a scheduled transaction.
## Steps to Reproduce
\`\`\`python
import asyncio
from hedera_agent_kit.shared.hedera_utils.hedera_parameter_normalizer import (
HederaParameterNormaliser,
)
# Simulate params as a dict — the standard input shape from an LLM agent
params = {
"token_id": "0.0.1234",
"serial_numbers": [1, 2],
"owner_account_id": "0.0.5678",
"scheduling_params": {
"is_scheduled": True,
"payer_account_id": "0.0.9999",
},
}
context = {}
client = None # not reached before the bug manifests
result = HederaParameterNormaliser.normalise_delete_non_fungible_token_allowance(
params, context, client
)
# Bug 1: scheduling_params is None despite being provided
print(result.scheduling_params) # → None (expected: ScheduleCreateParams)
\`\`\`
**Expected:** `result.scheduling_params` is a normalised `ScheduleCreateParams` object.
**Actual:** `result.scheduling_params` is `None`.
The same test with any other scheduling-capable normaliser (e.g.
`normalise_associate_token`) will correctly populate `scheduling_params`.
## Suggested Fix
\`\`\`python
@staticmethod
async def normalise_delete_non_fungible_token_allowance(
params: DeleteNonFungibleTokenAllowanceParameters,
context: Context,
client: Client,
) -> DeleteNftAllowanceParametersNormalised:
# ... existing logic unchanged up to the return statement ...
scheduling_params = None
if getattr(parsed_params, "scheduling_params", None):
if parsed_params.scheduling_params.is_scheduled:
scheduling_params = await HederaParameterNormaliser.normalise_scheduled_transaction_params(
parsed_params.scheduling_params, context, client
)
return DeleteNftAllowanceParametersNormalised(
nft_wipe=[nft_allowance],
transaction_memo=parsed_params.transaction_memo,
scheduling_params=scheduling_params,
)
\`\`\`
And at the call site in `delete_non_fungible_token_allowance.py`:
\`\`\`python
async def normalize_params(self, params, context, client):
return await HederaParameterNormaliser.normalise_delete_non_fungible_token_allowance(
params, context, client
)
\`\`\`
## Environment
- Repo: `hashgraph/hedera-agent-kit-py`
- Normaliser: `python/hedera_agent_kit/shared/hedera_utils/hedera_parameter_normalizer.py:2410`
- Tool: `python/hedera_agent_kit/plugins/core_token_plugin/delete_non_fungible_token_allowance.py:97`
Contributor guide
Research direction
Start at python/hedera_agent_kit/shared/hedera_utils/hedera_parameter_normalizer.py:2410 and compare its scheduling handling with the other scheduling-capable normalisers. Then inspect python/hedera_agent_kit/plugins/core_token_plugin/delete_non_fungible_token_allowance.py:97; done means scheduled parameters are normalized into ScheduleCreateParams and immediate calls still return normalized parameters.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 76/100