Skip to content

icp-cli: bindgen record fields with opt are undefined when empty, not null #409

Description

@marc0olo

skills/icp-cli/references/binding-generation.md:67-69 says bindgen's wrapper turns opt T into T | null and tells agents to "always use T | null". That holds for a plain opt, but not for a record field: an opt T field is an optional property that is undefined when empty. An agent following the rule writes if (r.value !== null), which is true for an empty field. The certified-variables helper had exactly this bug: it rejected a valid proof of absence (fixed in #407).

Verified against the @icp-sdk/bindgen 0.4.0 and 0.4.1 wrappers:

Candid position Wrapper type Empty value
argument, return value, vec element, variant payload: opt T T | null null
record field f : opt T f?: T undefined (field omitted)
nested opt opt T (not in a record) Some<T | null> | None None

Needed regardless of the bindgen PRs

These hold on 0.4.1 and after dfinity/icp-js-bindgen#197, dfinity/icp-js-bindgen#198 and dfinity/icp-js-bindgen#201:

  • Correct the section in binding-generation.md. T | null applies to a plain opt. A record field is optional and undefined when empty. To send an absent field, omit it or pass undefined.
  • Recommend == null or ?? for "is it empty". It is correct in every position and version, so an agent doesn't need to know which position it is in.
  • Add an eval for the record-field case. The existing case ("Candid opt T representation in bindgen") covers only a plain return value, and its expected result !== null stays correct.
  • Fix it in place, in icp-cli. Reconsider canister-calls: one skill for calling canisters, JS/TS first #406 moves binding-generation.md's opt section into canister-calls later; the move then carries the corrected text. Reconsider canister-calls: one skill for calling canisters, JS/TS first #406's own Candid↔TS table had the same gap (opt T → T | null only) and is corrected there, since it is the long-term home of the full mapping.

After the bindgen fixes are released

Until the bindgen PRs are decided, document only the first section.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions