fix(odata): type an external action's return value and its parameters - #381
Merged
Conversation
A `call external action` was written with neither the result variable's
type nor its parameters' types, so Mendix reported CE7269 ("the return
type for remote action '<x>' has changed") and CE7252 ("the parameters
... have changed"), and re-running CREATE OR MODIFY EXTERNAL ENTITIES
never cleared them.
It never could. Both codes are defined on CallExternalAction.cs
(extracted from Mendix.Modeler.Texts.dll, 11.13): they are raised by the
microflow ACTIVITY, not by the entity. That is what made the reported
remedy the wrong lever, and it cost the reporter a debugging session.
Two omissions of the same shape, each a DataTypes$ sub-document that was
never written:
- The return-type resolver mapped only EDM primitives and returned ""
for anything else, so an action returning an entity (or a collection
of them) got no VariableDataType at all. It now resolves to
DataTypes$ObjectType / DataTypes$ListType naming the external entity
imported for that type -- the same linkage the entity import writes.
- ExternalActionParameterMapping.ParameterType was never written,
though generated/metamodel declares it WITHOUT omitempty. Measured: a
call with ANY parameter, of any type, produced CE7252 plus one CE0117
"Error(s) in expression" per argument, because an argument cannot be
type-checked against an untyped parameter.
Measured on 11.13 against a contract with three action shapes: before, a
no-parameter entity return was CE7269, a one-string-parameter call was
CE7252 + 1x CE0117, and a two-parameter call CE7252 + 2x CE0117; after,
all three build at 0 errors. Reverting the return resolver reproduces
CE7269 verbatim.
`check --references` now resolves the call against the cached contract
too, so an unknown action, an undeclared argument, a missing parameter,
or an entity return whose entity has not been imported are reported with
the statement that fixes them.
Also: the catalog listed only entities stored as
Rest$ODataRemoteEntitySource, skipping every Rest$ODataEntityTypeSource
-- what CREATE EXTERNAL ENTITIES writes for any type the contract gives
no entity set, including an action's parameter and return types. The
consequence was worse than the under-count: contract_entities.
UsedByExternalEntity is filled by joining that table on RemoteName, so
for exactly those entities the column was structurally always empty and
read as "linked to nothing" whether or not the import had worked. That
column is the evidence the report was diagnosed from.
Fixes mendixlabs#1020
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
ako
force-pushed
the
claude/mxbuild-diagnostics-spike-emta6h
branch
from
September 3, 2026 21:04
76795bb to
c6b5fc2
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A
call external actionagainst a consumed OData service was written with neither the result variable's type nor its parameters' types. Mendix reportedCE7269andCE7252, and re-runningCREATE OR MODIFY EXTERNAL ENTITIESnever cleared them.Fixes mendixlabs#1020.
Why the reported remedy could never work
Both codes are defined on
CallExternalAction.cs— extracted from Mendix 11.13's ownMendix.Modeler.Texts.dll:ACTION_PARAMETERS_UNALIGNEDACTION_RETURN_TYPE_UNALIGNEDThey are raised by the microflow activity, not by the entity.
CREATE OR MODIFY EXTERNAL ENTITIESwrites entities, so it reports success and changes nothing relevant — which is exactly what the reporter observed and reasonably read as a linkage bug.Two omissions of the same shape
Each is a
DataTypes$sub-document that was never written:CE7269 — entity returns.
edmReturnTypeToKindmapped only EDM primitives and returned""for everything else, self-documented as "Complex / collection / entity-typed returns aren't yet mapped". An action returning an entity got noVariableDataTypeat all. It now resolves toDataTypes$ObjectType/DataTypes$ListTypenaming the external entity imported for that type — matching onRemoteServiceName+RemoteEntityName, the same linkage the entity import writes.CE7252 — parameters.
ExternalActionParameterMapping.ParameterTypewas never written, thoughgenerated/metamodeldeclares it withoutomitempty. This is larger than the report describes: a call with any parameter, of any type, produced CE7252 plus oneCE0117"Error(s) in expression" per argument — an argument cannot be type-checked against an untyped parameter. I found it by testing the missing-mandatory-property hypothesis a second time after it explained the first half.The catalog fix, and why the report's evidence was misleading
external_entitiescatalogued onlyRest$ODataRemoteEntitySource, skipping everyRest$ODataEntityTypeSource— whatCREATE EXTERNAL ENTITIESwrites for any type the contract gives no entity set: derived, abstract, contained, and an action's parameter and return types.The consequence was worse than the under-count.
contract_entities.UsedByExternalEntityis filled by joining that table onRemoteName, so for exactly those entities the column was structurally always empty — it read as "linked to nothing" whether or not the import had worked. That column is what the issue was diagnosed from. Both sources are catalogued now, and the reporter's query resolves (Airport → Ext.Airport).Worth noting generally: when a report cites one of our own derived columns as evidence, check the column can be non-empty for that case before believing it.
Verification
No
$metadatafixture in the repo declares an action at all, so this path had no end-to-end coverage. I served a contract frompython3 -m http.serveron loopback and pointedMetadataUrlat it, which makes the whole consumed-OData path testable locally.Measured on Mendix 11.13, three action shapes,
mx checkafter each:Controls: reverting the return resolver reproduces CE7269 verbatim on the same app; removing the Object/List writer cases makes the unit tests report
DataTypes$VoidType, which is precisely the reported symptom.Unit tests cover the writers on both engines (the modelsdk one asserts on the encoded document, so
EntityandParameterTypeare checked as stored keys rather than Go fields), the type-name normalisation, the call walker, and the catalog join — the last with a control proving an unimported type still reads as unlinked, so the join cannot pass by populating everything.make build, fullgo test ./...,make lint-go,make check-mdl(463 PASS, exit 0) andmake check-findingsall green.Also in this PR
check --referencesnow resolves the call against the cached contract, so an unknown action, an argument the action does not declare, a declared parameter left unsupplied, or an entity return whose entity has not been imported are reported with the statement that fixes them rather than surfacing a build later.Two constraints learned while building the fixture and worth knowing: Mendix's
call external actiontakes OData Actions only, not Functions (CE7251), and an unbound action needs an<ActionImport>in theEntityContaineror it is not part of the service's callable surface at all.Note on branching
PR #374 merged while this was in progress, so this branch was restarted from current
mainwithrebase --ontoand everything above was re-measured against that base. This commit is not part of #374.🤖 Generated with Claude Code
https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
Generated by Claude Code