Skip to content

fix: resolve InherentTypeViolation and improve Union type resolution for MI-Ballerina interoperability#37

Merged
thuva9872 merged 10 commits intowso2:mainfrom
iamvirul:fix-inherent-type-violation
Apr 16, 2026
Merged

fix: resolve InherentTypeViolation and improve Union type resolution for MI-Ballerina interoperability#37
thuva9872 merged 10 commits intowso2:mainfrom
iamvirul:fix-inherent-type-violation

Conversation

@iamvirul
Copy link
Copy Markdown
Member

Purpose

This PR addresses critical runtime issues where Ballerina function invocations from WSO2 Micro Integrator (MI) would fail due to InherentTypeViolation. This happens because generic data structures (maps/arrays) coming from the MI runtime were not being correctly cast into the specific inherent types required by the Ballerina runtime. Additionally, it improves the resolution of complex Union types and handling of Intersection types.

Resolves wso2/product-integrator-mi#4828

Goals

  • Ensure all data passed to Ballerina functions/methods has the correct inherent type to prevent runtime crashes.
  • Provide robust and strict resolution for Union types containing multiple record members.
  • Improve the accuracy of XML metadata generation for complex configurations.

Approach

  • Recursive Type Unwrapping: Updated DataTransformer to recursively unwrap Intersection and TypeReference types to reach the effective underlying type.
  • Inherent Type Preservation: Implemented createTypedRecordFromGeneric, createTypedArrayFromGeneric, and createTypedMapFromGeneric to explicitly recreate values with the expected Ballerina Type descriptors.
  • Strict Union Matching: Introduced a multi-pass strategy for Union resolution. Specifically, record members are now matched strictly (checking for extra fields in closed records and ensuring at least one field matches) to avoid "false positive" matches with the first record member in a Union.
  • Numeric Coercion: Added explicit logic to coerce Java Number types into the exact Ballerina numeric types (int, float, decimal).
  • Metadata Refinement: Updated XmlPropertyWriter to use a more consistent paramN_recordName naming convention and support recursive writing of nested record properties.

User stories

As an integration developer using the Ballerina MI Gen tool, I want my generated connectors to handle complex data structures (like Unions of records or readonly configurations) seamlessly without encountering runtime type violations when data is passed from the MI sequence into Ballerina.

Release note

Fixed InherentTypeViolation and Union resolution failures during Ballerina function execution from WSO2 Micro Integrator. This includes improved support for record inherent types, intersection types, and strict numeric type coercion.

Documentation

N/A. This is a core runtime reliability fix and does not change the user-facing CLI commands or Ballerina syntax.

Training

N/A

Certification

N/A

Marketing

N/A

Automation tests

  • Unit tests: Extensively updated DataTransformerTest.java (refactored 270+ lines) to replace reference equality checks with value equality and added 30+ new test cases for complex type conversion scenarios.
  • Integration tests: Verified against the full mi-tests suite, including new scenarios for Union types in TestUnionDataType.java.
  • Coverage: Improved coverage for edge cases in type conversion and parameter handling.

Security checks

Samples

Sample projects in tests/src/test/resources/ballerina/ such as unionProject and recordProject demonstrate the fixed behaviors.

Related PRs

N/A

Migrations (if applicable)

N/A

Test environment

  • JDK versions: 21
  • Operating systems: macOS
  • Ballerina Version: 2201.13.1

Learning

N/A

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 16, 2026

Warning

Rate limit exceeded

@iamvirul has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 10 minutes and 49 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 10 minutes and 49 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a367af15-c5bc-4d25-8801-76badc5961d7

📥 Commits

Reviewing files that changed from the base of the PR and between b6cb16e and 0ecbd66.

📒 Files selected for processing (5)
  • native/src/main/java/io/ballerina/stdlib/mi/BalConnectorConfig.java
  • native/src/main/java/io/ballerina/stdlib/mi/executor/DataTransformer.java
  • tests/src/test/java/io/ballerina/stdlib/mi/executor/DataTransformerTest.java
  • tests/src/test/java/io/ballerina/stdlib/mi/executor/ParamHandlerTest.java
  • tool-migen-cli/src/main/java/io/ballerina/mi/generator/XmlPropertyWriter.java

Walkthrough

Fixes InherentTypeViolation and union resolution failures during Ballerina function execution from Micro Integrator by implementing strict type conversions before invocation, improving union member selection through enhanced type unwrapping, and correcting property naming in configuration and union parameter handling.

Changes

Cohort / File(s) Summary
Type Conversion & Parameter Handling
native/src/main/java/io/ballerina/stdlib/mi/executor/DataTransformer.java, native/src/main/java/io/ballerina/stdlib/mi/executor/ParamHandler.java
Major refactoring of type conversion logic with support for INTERSECTION_TYPE and TYPE_REFERENCED_TYPE unwrapping, new createTypedMapFromGeneric method, enhanced union resolution with multi-pass strategy, and stricter record validation. ParamHandler now accepts callable parameter to enable proper type resolution during parameter assignment.
Execution & Configuration
native/src/main/java/io/ballerina/stdlib/mi/executor/BalExecutor.java, native/src/main/java/io/ballerina/stdlib/mi/BalConnectorConfig.java
Enhanced argument preparation to pass callable object for type resolution, improved error logging for BError results, and added conditional BMap handling with exception suppression in config assignment.
Type Symbol Utilities
tool-migen-cli/src/main/java/io/ballerina/mi/util/Utils.java
Extended actual type resolution to handle both TYPE_REFERENCE and INTERSECTION types through recursive unwrapping.
Generator Output Logic
tool-migen-cli/src/main/java/io/ballerina/mi/generator/JsonGenerator.java, tool-migen-cli/src/main/java/io/ballerina/mi/generator/XmlPropertyWriter.java
Updated enableCondition derivation for grouped fields and standardized parameter property naming from {connectionType}_recordName to param{index}_recordName scheme; fixed union member property value generation.
XML Template
tool-migen-cli/src/main/resources/balConnector/functions/functions_template.xml
Conditional union parameter rendering to suppress member definitions when typeDescriptor is set, preventing duplicate parameter generation.
Test Implementation
tests/src/test/java/io/ballerina/stdlib/mi/executor/DataTransformerTest.java, tests/src/test/java/io/ballerina/stdlib/mi/executor/ParamHandlerTest.java
Replaced assertSame with assertEquals, refactored mocking to verify typed array results, added strict record validation tests, updated signature calls with new callable parameter.
Test Resources & Configuration
tests/src/test/resources/ballerina/project.../Dependencies.toml, tests/src/test/resources/expected/.../*.json, tests/src/test/resources/expected/.../...\.xml
Updated dependency versions (http, observe, openapi), adjusted expected UI schema and XML function templates to reflect renamed components and updated paramType attributes for typedesc handling.
Documentation
CHANGELOG.md
Added four bug fixes documenting InherentTypeViolation prevention, union map pointer mismatch correction, typedesc-backed union XML generation, and grouped attribute condition handling.

Sequence Diagram

sequenceDiagram
    participant Synapse as Synapse/MI<br/>Properties
    participant BalExec as BalExecutor
    participant ParamHnd as ParamHandler
    participant DataTrans as DataTransformer
    participant Ballerina as Ballerina<br/>Runtime

    Synapse->>BalExec: invoke callable with args
    BalExec->>ParamHnd: setParameters(args, context, callable)
    ParamHnd->>ParamHnd: loop: for each param
    ParamHnd->>ParamHnd: check if BMap or BArray
    alt Type Conversion Needed
        ParamHnd->>DataTrans: getFunctionParameterType(callable, functionName, index)
        DataTrans-->>ParamHnd: expectedType
        ParamHnd->>DataTrans: convertValueToType(param, expectedType)
        Note over DataTrans: unwrap INTERSECTION_TYPE<br/>unwrap TYPE_REFERENCED_TYPE<br/>resolve UNION_TAG with multi-pass
        DataTrans-->>ParamHnd: converted value
        ParamHnd->>ParamHnd: assign converted param to args[i]
    else No Conversion
        ParamHnd->>ParamHnd: assign param as-is
    end
    ParamHnd-->>BalExec: prepared args
    BalExec->>Ballerina: invoke with prepared args
    alt Success
        Ballerina-->>BalExec: result
    else BError
        BalExec->>BalExec: log error message
        BalExec-->>BalExec: throw BallerinaExecutionException
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.78% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately reflects the main objectives of the PR: fixing InherentTypeViolation and improving Union type resolution for MI-Ballerina interoperability.
Description check ✅ Passed The PR description follows the template and covers all major sections including Purpose, Goals, Approach, User stories, Release notes, and test coverage details.
Linked Issues check ✅ Passed The PR code changes comprehensively address all objectives from issue #4828: preventing InherentTypeViolation through recursive type unwrapping and inherent type preservation, implementing strict Union member matching with multi-pass strategy, and improving numeric type coercion.
Out of Scope Changes check ✅ Passed All code changes are directly related to resolving issue #4828. Changes to DataTransformer, ParamHandler, BalExecutor, JsonGenerator, XmlPropertyWriter, and Utils implement the stated approach of type unwrapping, inherent type preservation, and Union resolution. Dependency updates and test fixture changes are necessary supporting modifications.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@iamvirul iamvirul marked this pull request as ready for review April 16, 2026 08:27
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Nitpick comments (1)
tests/src/test/java/io/ballerina/stdlib/mi/executor/ParamHandlerTest.java (1)

71-72: Please add coverage for the new callable-aware conversion path.

This update only verifies the new signature with null. It never exercises the BObject/Module lookup added in ParamHandler.setParameters(...), so regressions in the new type-conversion branch will slip through.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/src/test/java/io/ballerina/stdlib/mi/executor/ParamHandlerTest.java`
around lines 71 - 72, The test currently calls ParamHandler.setParameters(args,
context, null) and thus never exercises the callable-aware conversion path;
update the test to also invoke ParamHandler.setParameters with a non-null
callable container (e.g., a BObject or Module instance) so the BObject/Module
lookup in ParamHandler.setParameters is exercised, and add assertions verifying
the converted parameter values/state after the call to catch regressions in the
new type-conversion branch.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@native/src/main/java/io/ballerina/stdlib/mi/BalConnectorConfig.java`:
- Around line 153-166: The loop that builds connection args (using
paramHandler.getParameter and the local objectTypeName) never reshapes
BMap/BArray parameters into the expected inherent Ballerina types, so ensure
each param is converted before storing back into args: for each param returned
by paramHandler.getParameter in the loop, if param is a BMap or BArray, call the
existing conversion/transform helper used elsewhere (e.g., the
ParamHandler.convertValueToType or equivalent convertValueToType utility
referenced in the review) with context and the objectTypeName / the param's
expected type so it becomes the typed record/array; replace args[i] with the
converted value and handle exceptions (don’t swallow them silently) so
ValueCreator.createObjectValue(...) receives properly typed values similar to
the logic in io.ballerina.stdlib.mi.executor.ParamHandler.

In `@native/src/main/java/io/ballerina/stdlib/mi/executor/DataTransformer.java`:
- Around line 394-409: The catch block after convertValueToType currently logs
the exception and then falls back to returning the raw reconstructedBMap, which
can reintroduce InherentTypeViolation downstream (see ParamHandler usage);
change the behavior so that when convertValueToType(reconstructedBMap, recType)
throws, you throw a SynapseException (including the original exception message
or wrapping the exception) instead of returning the untyped value; apply the
same change to the analogous block that handles the other typed-record
conversion (the second convertValueToType/reconstructedBMap branch mentioned in
the comment).
- Around line 89-107: The conversion logic in DataTransformer that handles
TypeTags.INT_TAG and TypeTags.FLOAT_TAG currently uses ((Number)
sourceValue).longValue() which silently truncates fractional values (causing
lossy narrowing and wrong union-member selection); update the INT conversion to
detect fractional input (e.g., non-integer Number or BDecimal/BFloat with
fraction) and fail fast by throwing a type/coercion error instead of truncating,
and keep FLOAT conversion as doubleValue(); also add explicit handling for
TypeTags.DECIMAL_TAG in the union "Pass 2" coercion logic (the same pass that
currently handles numeric members) so decimal union members get a proper
numeric->decimal conversion (use BigDecimal construction from the numeric value)
rather than falling through to the string-only pass. Ensure you modify the
branches that reference TypeTags.INT_TAG, TypeTags.FLOAT_TAG,
TypeTags.DECIMAL_TAG and longValue()/doubleValue() in DataTransformer to
implement these checks and the new DECIMAL_TAG branch.
- Around line 198-233: createTypedRecordFromGeneric currently only copies
declared fields and drops undeclared entries; update it so when targetType is a
RecordType with a non-null/non-NEVER restFieldType (i.e., an open record) you
also iterate over genericMap keys that are not in targetType.getFields() and
insert them into typedRecord as rest fields: convert each undeclared value with
convertValueToType(genericValue, restFieldType) and put it under the same
BString key (ensuring you skip keys already handled by the declared-field loop);
keep the existing strict-mode closed-record validation unchanged.

In
`@tests/src/test/java/io/ballerina/stdlib/mi/executor/DataTransformerTest.java`:
- Around line 397-440: The test testCreateTypedRecordFromGeneric_ClosedRecord
doesn't exercise the closed-record rejection path because it mocks StructureType
and calls the non-strict overload; update the test to mock RecordType (use
RecordType instead of StructureType), ensure targetType instanceof RecordType by
returning that mock from targetType, and call the strict overload of
DataTransformer.createTypedRecordFromGeneric(...) with strict=true (or otherwise
invoke the API that enforces closed-record semantics) while providing a
sourceMap that contains an extra key ("unknown") so the method will hit the
closed-record branch and you can assert the expected rejection/behavior.

In `@tests/src/test/resources/expected/project7/functions/component.xml`:
- Around line 66-68: The <component name="postUsersMessages_importByUserId">
entry has a mismatched <file> value — update the <file> element currently set to
"postUsersMessages_importByUserId.xml" to exactly match the actual fixture
filename "postusersmessages_Importbyuserid.xml" (preserving case) so the file
reference in component.xml points to the real fixture and avoids case-sensitive
filesystem failures.

In
`@tool-migen-cli/src/main/java/io/ballerina/mi/generator/XmlPropertyWriter.java`:
- Around line 113-119: The code in XmlPropertyWriter unconditionally
dereferences parameter.typeInfo when writing record metadata (in the case
"record"), which crashes for params with no typeInfo and emits "null" strings;
update the "record" branch to check parameter.typeInfo != null before appending
the recordName/recordModule/recordOrg/recordVersion properties (and individually
guard each field if needed) so you only emit those <property .../> lines when
typeInfo and its non-null subfields exist, keeping the existing parameter.name
and parameter.typeName lines unchanged; reference the case "record" block, the
parameter variable, and the typeInfo property when making this change.

---

Nitpick comments:
In `@tests/src/test/java/io/ballerina/stdlib/mi/executor/ParamHandlerTest.java`:
- Around line 71-72: The test currently calls ParamHandler.setParameters(args,
context, null) and thus never exercises the callable-aware conversion path;
update the test to also invoke ParamHandler.setParameters with a non-null
callable container (e.g., a BObject or Module instance) so the BObject/Module
lookup in ParamHandler.setParameters is exercised, and add assertions verifying
the converted parameter values/state after the call to catch regressions in the
new type-conversion branch.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: cbfc0c1f-762e-4486-8b70-ef0cf493f88c

📥 Commits

Reviewing files that changed from the base of the PR and between 807ddd1 and b6cb16e.

📒 Files selected for processing (21)
  • CHANGELOG.md
  • native/src/main/java/io/ballerina/stdlib/mi/BalConnectorConfig.java
  • native/src/main/java/io/ballerina/stdlib/mi/executor/BalExecutor.java
  • native/src/main/java/io/ballerina/stdlib/mi/executor/DataTransformer.java
  • native/src/main/java/io/ballerina/stdlib/mi/executor/ParamHandler.java
  • tests/src/test/java/io/ballerina/stdlib/mi/executor/DataTransformerTest.java
  • tests/src/test/java/io/ballerina/stdlib/mi/executor/ParamHandlerTest.java
  • tests/src/test/resources/ballerina/project5/Dependencies.toml
  • tests/src/test/resources/ballerina/project7/Dependencies.toml
  • tests/src/test/resources/expected/project4/uischema/project4_Client.json
  • tests/src/test/resources/expected/project7/functions/component.xml
  • tests/src/test/resources/expected/project7/functions/postusersmessages_Importbyuserid.xml
  • tests/src/test/resources/expected/project7/uischema/postusersmessages_Importbyuserid.json
  • tests/src/test/resources/expected/typedescProject/functions/processTypedescAnydata.xml
  • tests/src/test/resources/expected/typedescProject/functions/processTypedescRecord.xml
  • tests/src/test/resources/expected/typedescProject/functions/processTypedescString.xml
  • tests/src/test/resources/expected/typedescProject/functions/processTypedescUnion.xml
  • tool-migen-cli/src/main/java/io/ballerina/mi/generator/JsonGenerator.java
  • tool-migen-cli/src/main/java/io/ballerina/mi/generator/XmlPropertyWriter.java
  • tool-migen-cli/src/main/java/io/ballerina/mi/util/Utils.java
  • tool-migen-cli/src/main/resources/balConnector/functions/functions_template.xml

Comment thread native/src/main/java/io/ballerina/stdlib/mi/BalConnectorConfig.java Outdated
Comment thread tests/src/test/resources/expected/project7/functions/component.xml
Comment thread tool-migen-cli/src/main/java/io/ballerina/mi/generator/XmlPropertyWriter.java Outdated
@thuva9872 thuva9872 merged commit 9287aeb into wso2:main Apr 16, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Ballerina][MI] InherentTypeViolation and Union resolution failures during Ballerina function execution from Micro Integrator

2 participants