HTTP Request Node Key-Value Editor#
The key-value editor is the UI component used in Dify's HTTP Request workflow node to edit params, headers, and form-data body fields as interactive rows of key/value pairs. It supports variable references via a Lexical-based rich-text input and optionally a file type selector for form-data body entries.
Component Structure#
| File | Role |
|---|---|
key-value-edit/item.tsx | Single row (key cell + optional type dropdown + value cell) |
key-value-edit/input-item.tsx | Rich-text input cell supporting variable interpolation (wraps input-support-select-var) |
hooks/use-key-value-list.ts | Hook that owns list state, parses the serialized string, and exposes setList / addItem |
The parent list component renders KeyValueItem for each entry. KeyValueItem receives instanceId, payload, onChange, onRemove, isLastItem, and onAdd . Each row's key and value cells are rendered via InputItem, which passes the instanceId downstream as http-key-{instanceId} / http-value-{instanceId} to the Lexical editor so it can be stably keyed .
When isSupportFile is true, an additional type dropdown (text / file) is shown between the key and value cells. Selecting file replaces the value InputItem with a VarReferencePicker .
State Model: String Round-Trip#
use-key-value-list holds the list in local React state but the authoritative store is a newline-joined string (key:value\nkey:value). Every mutation calls stringifyList and bubbles the string up via onChange . On the next render, a microtask useEffect re-parses the string back into a list and replaces state if the serialized forms differ .
This round-trip model is the root cause of several compound bugs described below.
Known Bugs and Fixes#
1. Row remounting / lost keystrokes (open β PR #39552)#
Issue: Typing into the trailing (new) row destroyed the row β the character was lost and any key already entered disappeared. Pasting worked; typing did not.
Root cause: Two compounding issues in use-key-value-list.ts :
- The microtask
useEffectre-derived the list from the string on every change and reassigned every row'sid, remounting the Lexical editor mid-keystroke and dropping the typed character. addItemclosed over a stalelist, so anonChangeimmediately followed byonAddlet the add overwrite the just-typed value.
Fix (PR #39552): Add a listRef mirror so addItem no longer reads stale state. In the reconciliation useEffect, preserve existing row ids positionally (prev[index]?.id || item.id) so that only genuinely new rows receive a fresh id .
2. Colon truncation in values (fixed β PRs #38861, #39165)#
Issue: Form-data values containing colons (e.g., URLs like https://example.com:8080/path, Bearer tokens abc:def) were silently truncated to the segment before the first colon on node reload. The truncated value was then persisted, corrupting the workflow .
Root cause: transformToBodyPayload in utils.ts split each line with const [key, value] = item.split(':'), keeping only the first segment. use-key-value-list.ts had the same flaw.
Fix: Both sites now use const [key, ...others] = item.split(':') with others.join(':') to reconstruct the full value . PR #39165 also added splitFirst helper and regression tests in use-key-value-list.test.ts and utils.spec.ts.
3. Unintended row append on empty-value click (fixed β PR #35051)#
Issue: Clicking anywhere in an empty value cell triggered onAdd(), appending a new row even though no content was entered.
Fix: handleValueContainerClick now only calls onAdd() when isLastItem && hasValuePayload β i.e., the row already has content . Similarly, handleChange calls onAdd() only when the value transitions from empty to non-empty on the last row .
4. Type dropdown component instability (fixed β PR #35304)#
Issue: The PortalSelect component used for the text/file type cell caused rendering and accessibility issues.
Fix: Replaced with the shared Select / SelectTrigger / SelectContent component family; added value={payload.type ?? 'text'} as a stable default to prevent undefined states .
Structural Limitation#
The newline-joined-string storage (key:value\nβ¦) is a known architectural constraint. Among its consequences:
- Keys or values containing newlines cannot be represented.
- Every render cycle runs a full serialize β deserialize β compare cycle.
- Pressing Enter in the trailing row's key field still migrates the existing value into the newly-created row (documented as a follow-up in issue #39551) .
Changing this model requires migrating the node's persisted data format, which is explicitly out of scope for the incremental bug fixes above.
Key Source Files#
- Row component:
item.tsx - State hook:
use-key-value-list.ts - Body migration utility:
web/app/components/workflow/nodes/http/utils.ts(see PRs #38861, #39165) - Tests:
web/app/components/workflow/nodes/http/__tests__/(integration, utils, use-key-value-list)