Skip to content

Fixes #26869: Defensive type handling in AirflowRESTClient to prevent ClassCastException#27019

Merged
pmbrull merged 3 commits intoopen-metadata:mainfrom
RajdeepKushwaha5:fix/26869-yaml-integer-classcastexception
Apr 13, 2026
Merged

Fixes #26869: Defensive type handling in AirflowRESTClient to prevent ClassCastException#27019
pmbrull merged 3 commits intoopen-metadata:mainfrom
RajdeepKushwaha5:fix/26869-yaml-integer-classcastexception

Conversation

@RajdeepKushwaha5
Copy link
Copy Markdown
Contributor

@RajdeepKushwaha5 RajdeepKushwaha5 commented Apr 3, 2026

Describe your changes:

Fixes #26869

Problem

AirflowRESTClient constructor performs direct (Integer) and (String) casts on values read from config.getParameters().getAdditionalProperties(). When SnakeYAML parses unquoted numeric values like timeout: 60 in openmetadata.yaml, it deserializes them as java.lang.Integer. However, the code later casts these values with (String), causing a ClassCastException at runtime:

java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String

This crashes the server on startup when pipeline service client parameters contain unquoted numeric YAML values.

Solution

Instead of fragile direct casts, this PR introduces two defensive helper methods in AirflowRESTClient:

  • getStringParam(params, key) — Retrieves a value and converts it to String via value.toString(), safely handling Integer, Long, String, or any other type SnakeYAML may produce.
  • getIntParam(params, key, defaultValue) — Retrieves a value and converts it to int via Integer.parseInt(value.toString()). Logs a LOG.warn with the actual value if parsing fails, then falls back to the provided default.

Additional improvements:

  • Null-safe handling of both config.getParameters() and config.getParameters().getAdditionalProperties() using Collections.emptyMap() fallback — prevents NPE when Parameters is present but additionalProperties is null.
  • Credentials validation: throws PipelineServiceClientException early if username or password is null after parameter extraction.

Testing

Added 7 new unit tests in AirflowRESTClientTest:

Test Scenario
constructorHandlesStringTimeout timeout provided as "60" (String)
constructorHandlesIntegerTimeout timeout provided as 60 (Integer)
constructorHandlesLongTimeout timeout provided as 60L (Long)
constructorHandlesNullParameters config.getParameters() is null
constructorHandlesNonNumericTimeout timeout provided as "abc" — exercises warn+fallback branch
constructorHandlesNullAdditionalProperties Parameters is non-null but additionalProperties is null
constructorHandlesMissingCredentials missing username/password throws exception

All tests pass locally.

Type of change:

  • Bug fix
  • Improvement
  • New feature
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation

Checklist:

  • I have read the CONTRIBUTING document.
  • My PR title is Fixes <issue-number>: <short explanation>
  • I have commented on my code, particularly in hard-to-understand areas.
  • For JSON Schema changes: I updated the migration scripts or explained why it is not needed.
  • I have added a test that covers the exact scenario we are fixing. For complex issues, comment the issue number in the test for future reference.

Copilot AI review requested due to automatic review settings April 3, 2026 10:08
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 3, 2026

Hi there 👋 Thanks for your contribution!

The OpenMetadata team will review the PR shortly! Once it has been labeled as safe to test, the CI workflows
will start executing and we'll be able to make sure everything is working as expected.

Let us know if you need any help!

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes ClassCastException during pipeline client initialization by making Airflow/K8s pipeline client parameter handling resilient to YAML-parsed numeric types and by adjusting default config values.

Changes:

  • Updated AirflowRESTClient to read parameters via safe string/int helpers instead of direct casts.
  • Added unit tests ensuring timeout parameter types (String, Integer, Long) don’t break client construction.
  • Updated example YAML configs to quote numeric defaults in pipelineServiceClientConfiguration.parameters.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.

File Description
openmetadata-service/src/main/java/org/openmetadata/service/clients/pipeline/airflow/AirflowRESTClient.java Replaces fragile casts with getStringParam/getIntParam helpers for parameter parsing.
openmetadata-service/src/test/java/org/openmetadata/service/clients/pipeline/airflow/AirflowRESTClientTest.java Adds constructor tests covering multiple numeric/string timeout representations.
conf/openmetadata.yaml Quotes numeric defaults under pipelineServiceClientConfiguration.parameters.
conf/openmetadata-s3-logs.yaml Quotes numeric defaults for Airflow timeout parameters in the S3 logs example config.

Comment on lines +609 to +611
runAsUser: ${K8S_RUN_AS_USER:-"1000"}
runAsGroup: ${K8S_RUN_AS_GROUP:-"1000"}
fsGroup: ${K8S_FS_GROUP:-"1000"}
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

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

Same issue as above for securityContext IDs: quoting only the default doesn’t prevent numeric env var values from being parsed as numbers. Wrap the whole substitution in quotes (e.g. runAsUser: "${K8S_RUN_AS_USER:-1000}", etc.) so YAML always yields strings for parameters.*.

Suggested change
runAsUser: ${K8S_RUN_AS_USER:-"1000"}
runAsGroup: ${K8S_RUN_AS_GROUP:-"1000"}
fsGroup: ${K8S_FS_GROUP:-"1000"}
runAsUser: "${K8S_RUN_AS_USER:-1000}"
runAsGroup: "${K8S_RUN_AS_GROUP:-1000}"
fsGroup: "${K8S_FS_GROUP:-1000}"

Copilot uses AI. Check for mistakes.
@RajdeepKushwaha5 RajdeepKushwaha5 force-pushed the fix/26869-yaml-integer-classcastexception branch from 7cfc3f7 to d9d0d64 Compare April 3, 2026 10:37
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 3, 2026

Hi there 👋 Thanks for your contribution!

The OpenMetadata team will review the PR shortly! Once it has been labeled as safe to test, the CI workflows
will start executing and we'll be able to make sure everything is working as expected.

Let us know if you need any help!

@RajdeepKushwaha5 RajdeepKushwaha5 force-pushed the fix/26869-yaml-integer-classcastexception branch from e62eef0 to b9b7645 Compare April 9, 2026 04:16
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 9, 2026

Hi there 👋 Thanks for your contribution!

The OpenMetadata team will review the PR shortly! Once it has been labeled as safe to test, the CI workflows
will start executing and we'll be able to make sure everything is working as expected.

Let us know if you need any help!

Copilot AI review requested due to automatic review settings April 9, 2026 04:23
@RajdeepKushwaha5 RajdeepKushwaha5 force-pushed the fix/26869-yaml-integer-classcastexception branch from b9b7645 to b3b2c92 Compare April 9, 2026 04:23
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 9, 2026

Hi there 👋 Thanks for your contribution!

The OpenMetadata team will review the PR shortly! Once it has been labeled as safe to test, the CI workflows
will start executing and we'll be able to make sure everything is working as expected.

Let us know if you need any help!

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

@RajdeepKushwaha5 RajdeepKushwaha5 force-pushed the fix/26869-yaml-integer-classcastexception branch from b3b2c92 to 7935d7a Compare April 9, 2026 04:29
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 9, 2026

Hi there 👋 Thanks for your contribution!

The OpenMetadata team will review the PR shortly! Once it has been labeled as safe to test, the CI workflows
will start executing and we'll be able to make sure everything is working as expected.

Let us know if you need any help!

@RajdeepKushwaha5 RajdeepKushwaha5 changed the title Fixes #26869: Quote numeric YAML defaults to prevent ClassCastException Fixes #26869: Defensive type handling in AirflowRESTClient to prevent ClassCastException Apr 9, 2026
@pmbrull pmbrull added the safe to test Add this label to run secure Github workflows on PRs label Apr 9, 2026
pmbrull
pmbrull previously approved these changes Apr 9, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 9, 2026

🟡 Playwright Results — all passed (29 flaky)

✅ 3595 passed · ❌ 0 failed · 🟡 29 flaky · ⏭️ 207 skipped

Shard Passed Failed Flaky Skipped
🟡 Shard 1 455 0 2 2
🟡 Shard 2 641 0 2 32
🟡 Shard 3 640 0 9 26
🟡 Shard 4 620 0 7 47
🟡 Shard 5 608 0 1 67
🟡 Shard 6 631 0 8 33
🟡 29 flaky test(s) (passed on retry)
  • Features/DataAssetRulesDisabled.spec.ts › Verify the DashboardDataModel entity item action after rules disabled (shard 1, 1 retry)
  • Pages/UserCreationWithPersona.spec.ts › Create user with persona and verify on profile (shard 1, 1 retry)
  • Features/AdvancedSearch.spec.ts › Verify Rule functionality for field Status with OR operator (shard 2, 1 retry)
  • Features/ChangeSummaryBadge.spec.ts › Automated badge should appear on entity description with Automated source (shard 2, 1 retry)
  • Features/IncidentManager.spec.ts › Complete Incident lifecycle with table owner (shard 3, 1 retry)
  • Features/Permissions/GlossaryPermissions.spec.ts › Team-based permissions work correctly (shard 3, 1 retry)
  • Features/RestoreEntityInheritedFields.spec.ts › Validate restore with Inherited domain and data products assigned (shard 3, 1 retry)
  • Features/RestoreEntityInheritedFields.spec.ts › Validate restore with Inherited domain and data products assigned (shard 3, 1 retry)
  • Features/RestoreEntityInheritedFields.spec.ts › Validate restore with Inherited domain and data products assigned (shard 3, 2 retries)
  • Features/RestoreEntityInheritedFields.spec.ts › Validate restore with Inherited domain and data products assigned (shard 3, 2 retries)
  • Features/RTL.spec.ts › Verify Following widget functionality (shard 3, 1 retry)
  • Flow/CustomizeWidgets.spec.ts › Data Assets Widget (shard 3, 1 retry)
  • Flow/PersonaFlow.spec.ts › Set default persona for team should work properly (shard 3, 1 retry)
  • Pages/Customproperties-part2.spec.ts › entityReferenceList shows item count, scrollable list, no expand toggle (shard 4, 1 retry)
  • Pages/DataContracts.spec.ts › Create Data Contract and validate for Table (shard 4, 2 retries)
  • Pages/DataContractsSemanticRules.spec.ts › Validate Description Rule Is_Not_Set (shard 4, 1 retry)
  • Pages/DataContractsSemanticRules.spec.ts › Validate DataProduct Rule Any_In (shard 4, 1 retry)
  • Pages/Domains.spec.ts › Rename domain with subdomains attached verifies subdomain accessibility (shard 4, 1 retry)
  • Pages/Domains.spec.ts › Rename domain with assets (tables, topics, dashboards) preserves associations (shard 4, 1 retry)
  • Pages/Entity.spec.ts › Tag Add, Update and Remove (shard 4, 1 retry)
  • Pages/ExploreTree.spec.ts › Verify Database and Database Schema available in explore tree (shard 5, 1 retry)
  • Pages/Glossary.spec.ts › Column dropdown drag-and-drop functionality for Glossary Terms table (shard 6, 1 retry)
  • Pages/HyperlinkCustomProperty.spec.ts › should display URL when no display text is provided (shard 6, 1 retry)
  • Pages/Lineage/DataAssetLineage.spec.ts › verify create lineage for entity - Stored Procedure (shard 6, 1 retry)
  • Pages/Lineage/LineageFilters.spec.ts › Verify lineage schema filter selection (shard 6, 1 retry)
  • Pages/Lineage/LineageRightPanel.spec.ts › Verify custom properties tab IS visible for supported type: searchIndex (shard 6, 1 retry)
  • Pages/ODCSImportExport.spec.ts › Multi-object ODCS contract - object selector shows all schema objects (shard 6, 1 retry)
  • Pages/Users.spec.ts › Permissions for table details page for Data Consumer (shard 6, 1 retry)
  • VersionPages/EntityVersionPages.spec.ts › Directory (shard 6, 1 retry)

📦 Download artifacts

How to debug locally
# Download playwright-test-results-<shard> artifact and unzip
npx playwright show-trace path/to/trace.zip    # view trace

Copilot AI review requested due to automatic review settings April 10, 2026 06:12
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

Comment on lines +80 to +85
Map<String, Object> params =
config.getParameters() != null
? config.getParameters().getAdditionalProperties()
: Collections.emptyMap();
this.username = getStringParam(params, USERNAME_KEY);
this.password = getStringParam(params, PASSWORD_KEY);
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

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

config.getParameters() is null-checked, but config.getParameters().getAdditionalProperties() can still be null in some generated config POJOs; in that case params becomes null and getStringParam(params, …) will throw a NPE before the intended credential validation. Consider defaulting to Collections.emptyMap() when getAdditionalProperties() is null as well (and add a unit test for this case).

Copilot uses AI. Check for mistakes.
Comment on lines +114 to 120
Map<String, Object> params =
config.getParameters() != null
? config.getParameters().getAdditionalProperties()
: Collections.emptyMap();
String truststorePath = getStringParam(params, TRUSTSTORE_PATH_KEY);
String truststorePassword = getStringParam(params, TRUSTSTORE_PASSWORD_KEY);

Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

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

Same null-safety concern here: if config.getParameters() is non-null but getAdditionalProperties() is null, params will be null and getStringParam(params, …) will NPE. Prefer falling back to an empty map when getAdditionalProperties() is null.

Copilot uses AI. Check for mistakes.
Add null-safety check for getAdditionalProperties() in both the
constructor and createAirflowSSLContext(). While the generated
Parameters class initializes the field, deserialization could
produce a null value. Falls back to emptyMap gracefully.

Add constructorHandlesNullAdditionalProperties test to cover the
case where Parameters is non-null but additionalProperties is null.
Copilot AI review requested due to automatic review settings April 13, 2026 06:39
@gitar-bot
Copy link
Copy Markdown

gitar-bot bot commented Apr 13, 2026

Code Review ✅ Approved 2 resolved / 2 findings

Defensive type handling in AirflowRESTClient now prevents ClassCastException and null pointer exceptions by adding validation to getIntParam and getAdditionalProperties(). Both issues have been resolved.

✅ 2 resolved
Quality: getIntParam silently swallows invalid config values

📄 openmetadata-service/src/main/java/org/openmetadata/service/clients/pipeline/airflow/AirflowRESTClient.java:129-139
When getIntParam catches a NumberFormatException (e.g., timeout: "abc"), it silently returns the default value with no log message. This could mask configuration errors that are difficult to debug in production — the operator sets a value, but it's silently ignored.

Consider logging a warning so misconfigurations are visible.

Edge Case: getAdditionalProperties() can return null, causing NPE

📄 openmetadata-service/src/main/java/org/openmetadata/service/clients/pipeline/airflow/AirflowRESTClient.java:80-83 📄 openmetadata-service/src/main/java/org/openmetadata/service/clients/pipeline/airflow/AirflowRESTClient.java:114-117
The code checks config.getParameters() != null but does not check whether getAdditionalProperties() itself returns null. Other code in the codebase (e.g., ApplicationHandler.java:79-81) explicitly guards against both being null. If getParameters() is non-null but getAdditionalProperties() returns null, the subsequent params.get(...) calls in getStringParam/getIntParam will throw a NullPointerException.

Options

Display: compact → Showing less information.

Comment with these commands to change:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

@sonarqubecloud
Copy link
Copy Markdown

@pmbrull pmbrull merged commit 4bf567b into open-metadata:main Apr 13, 2026
50 of 51 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

safe to test Add this label to run secure Github workflows on PRs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ClassCastException: Integer cannot be cast to String in PipelineServiceClientFactory due to unquoted numeric YAML defaults

4 participants