Skip to content

Conversation

@ehsavoie
Copy link
Collaborator

  • Add @NullMarked to io.a2a.spec package
  • Apply @nullable annotations to optional fields and parameters
  • Move builder validation to build() methods using Assert.checkNotNullParam()
  • Fix null-safety issues in RequestContext ID generation
  • Add null checks for push notification configuration
  • Simplify REST route handling for push notification configs
  • Update tests to provide required MessageSendConfiguration

@ehsavoie ehsavoie requested review from jmesnil and kabir February 10, 2026 13:05
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @ehsavoie, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly improves the null-safety and robustness of the spec module and related components by migrating to JSpecify annotations. This involved systematically applying @Nullable annotations, centralizing parameter validation within builder patterns, and resolving several null-related issues in core functionalities like ID generation and push notification handling. The changes aim to prevent NullPointerExceptions and provide clearer API contracts.

Highlights

  • JSpecify Null-Safety Migration: The io.a2a.spec package has been marked with @NullMarked, and @Nullable annotations have been applied to optional fields and parameters across various classes to enhance null-safety.
  • Builder Validation Refactoring: Validation logic in builder classes has been moved to their respective build() methods, utilizing Assert.checkNotNullParam() to ensure required parameters are non-null at object construction.
  • RequestContext ID Generation Fixes: Null-safety issues related to RequestContext ID generation have been addressed, ensuring proper handling and assignment of task and context IDs.
  • Push Notification Configuration Enhancements: Explicit null checks were added for push notification configurations in DefaultRequestHandler and REST route handling was simplified by removing a redundant method and adding a configId check.
  • Test Updates: Existing tests for RequestContext and JSONRPCHandler were updated to provide the now-required MessageSendConfiguration in MessageSendParams.
Changelog
  • common/src/main/java/io/a2a/util/Assert.java
    • Added JSpecify's @nullable import.
    • Updated checkNotNullParam and checkNotNullParamChecked methods to explicitly handle nullable values and ensure non-null return types.
    • Annotated isNullOrStringOrInteger parameter with @nullable.
  • jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/json/JsonUtil.java
    • Removed unused java.util.Map import.
    • Added null-check for message when constructing A2AError instances.
    • Modified default error code for A2AError in processError to INTERNAL_ERROR_CODE.
  • reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java
    • Added a null or empty check for configId in getTaskPushNotificationConfiguration.
    • Removed the redundant getTaskPushNotificationConfigurationWithoutId method.
    • Simplified the regex for listTaskPushNotificationConfigurations to handle optional trailing slashes.
  • server-common/src/main/java/io/a2a/server/agentexecution/RequestContext.java
    • Refactored taskId and contextId generation logic to assign values directly from checkOrGenerate methods.
    • Changed checkOrGenerateTaskId and checkOrGenerateContextId to return @Nullable String.
  • server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java
    • Added explicit null checks for pushConfigStore, params.configuration(), and params.configuration().pushNotificationConfig() in push notification handling logic.
    • Removed the shouldAddPushInfo helper method.
    • Added a null check for updatedTask.artifacts() before accessing its size.
  • server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java
    • Added a null check for nextPageToken when constructing ListTaskPushNotificationConfigParams.
  • server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStore.java
    • Added a null check for config.id() before calling equals.
  • server-common/src/main/java/io/a2a/server/tasks/TaskUpdater.java
    • Changed taskId and contextId fields to non-nullable.
    • Used Assert.checkNotNullParam in the constructor to ensure taskId and contextId are not null.
  • server-common/src/test/java/io/a2a/server/agentexecution/RequestContextTest.java
    • Added a defaultConfiguration helper method to create MessageSendConfiguration instances.
    • Updated various test cases to include a defaultConfiguration() when building MessageSendParams.
  • spec-grpc/src/main/java/io/a2a/grpc/utils/JSONRPCUtils.java
    • Added null checks for the message parameter when creating A2AError instances.
  • spec/src/main/java/io/a2a/spec/A2AError.java
    • Added JSpecify's @nullable import.
    • Annotated the data field as @Nullable.
    • Updated the constructor to use Assert.checkNotNullParam for message and code.
  • spec/src/main/java/io/a2a/spec/A2AProtocolError.java
    • Added JSpecify's @nullable import.
    • Annotated the url field as @Nullable.
    • Updated the constructor to accept @Nullable data and url parameters.
  • spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java
    • Added JSpecify's @nullable import.
    • Annotated the description field as @Nullable.
    • Updated the builder to use Assert.checkNotNullParam for location and name.
  • spec/src/main/java/io/a2a/spec/AgentCapabilities.java
    • Added JSpecify's @nullable import.
    • Annotated the extensions field as @Nullable.
    • Updated the builder's extensions field to be nullable.
  • spec/src/main/java/io/a2a/spec/AgentCard.java
    • Added JSpecify's @nullable import and java.util.Collections import.
    • Annotated several fields (provider, documentationUrl, securitySchemes, securityRequirements, iconUrl, signatures) as @Nullable.
    • Updated builder fields to be nullable.
    • Initialized collection fields with Collections.emptyList() if null in the builder's copy constructor.
    • Used Assert.checkNotNullParam for required fields (name, description, version, capabilities, defaultInputModes, defaultOutputModes, skills, supportedInterfaces) in the build() method.
  • spec/src/main/java/io/a2a/spec/AgentCardSignature.java
    • Added JSpecify's @nullable import.
    • Annotated the header field as @Nullable.
    • Updated the builder to use Assert.checkNotNullParam for protectedHeader and signature.
  • spec/src/main/java/io/a2a/spec/AgentExtension.java
    • Added JSpecify's @nullable import.
    • Annotated description and params fields as @Nullable.
    • Updated the builder to use Assert.checkNotNullParam for uri.
  • spec/src/main/java/io/a2a/spec/AgentSkill.java
    • Added JSpecify's @nullable import.
    • Annotated several fields (examples, inputModes, outputModes, securityRequirements) as @Nullable.
    • Reordered null checks in the compact constructor.
    • Used Assert.checkNotNullParam for required fields (id, name, description, tags) in the build() method.
  • spec/src/main/java/io/a2a/spec/Artifact.java
    • Added JSpecify's @nullable import.
    • Annotated several fields (name, description, metadata, extensions) as @Nullable.
    • Updated the builder to use Assert.checkNotNullParam for artifactId and parts.
  • spec/src/main/java/io/a2a/spec/ContentTypeNotSupportedError.java
    • Added JSpecify's @nullable import.
    • Updated the constructor to accept @Nullable code, message, and data parameters.
  • spec/src/main/java/io/a2a/spec/DataPart.java
    • Added JSpecify's @nullable import.
    • Annotated the data field as @Nullable.
    • Updated the builder to use Assert.checkNotNullParam for data.
  • spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigParams.java
    • Added JSpecify's @nullable import.
    • Annotated builder fields (id, pushNotificationConfigId, tenant) as @Nullable.
    • Used Assert.checkNotNullParam for id, pushNotificationConfigId, and tenant in the build() method.
  • spec/src/main/java/io/a2a/spec/ExtendedAgentCardNotConfiguredError.java
    • Added JSpecify's @nullable import.
    • Updated the constructor to accept @Nullable code, message, and data parameters.
  • spec/src/main/java/io/a2a/spec/ExtensionSupportRequiredError.java
    • Added JSpecify's @nullable import.
    • Updated the constructor to accept @Nullable code, message, and data parameters.
  • spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigParams.java
    • Added JSpecify's @nullable import.
    • Annotated builder fields (id, pushNotificationConfigId, tenant) as @Nullable.
    • Used Assert.checkNotNullParam for id, pushNotificationConfigId, and tenant in the build() method.
  • spec/src/main/java/io/a2a/spec/HTTPAuthSecurityScheme.java
    • Added JSpecify's @nullable import.
    • Annotated bearerFormat and description fields as @Nullable.
    • Updated the builder to use Assert.checkNotNullParam for scheme.
  • spec/src/main/java/io/a2a/spec/InternalError.java
    • Added JSpecify's @nullable import.
    • Updated constructors to accept @Nullable code, message, and data parameters.
  • spec/src/main/java/io/a2a/spec/InvalidAgentResponseError.java
    • Added JSpecify's @nullable import.
    • Updated the constructor to accept @Nullable code, message, and data parameters.
  • spec/src/main/java/io/a2a/spec/InvalidParamsError.java
    • Added JSpecify's @nullable import.
    • Updated constructors to accept @Nullable code, message, and data parameters.
  • spec/src/main/java/io/a2a/spec/InvalidRequestError.java
    • Added JSpecify's @nullable import.
    • Updated the constructor to accept @Nullable code, message, and data parameters.
  • spec/src/main/java/io/a2a/spec/JSONParseError.java
    • Added JSpecify's @nullable import.
    • Updated the constructor to accept @Nullable code, message, and data parameters.
  • spec/src/main/java/io/a2a/spec/ListTasksParams.java
    • Added io.a2a.util.Assert import.
    • Removed explicit null check for tenant in the compact constructor.
    • Used Assert.checkNotNullParam for tenant in the build() method.
  • spec/src/main/java/io/a2a/spec/Message.java
    • Added JSpecify's @nullable import.
    • Annotated several fields (contextId, taskId, referenceTaskIds, metadata, extensions) as @Nullable.
    • Reordered metadata and extensions initialization in the compact constructor.
    • Used Assert.checkNotNullParam for role and parts in the build() method.
  • spec/src/main/java/io/a2a/spec/MessageSendConfiguration.java
    • Annotated acceptedOutputModes, historyLength, and pushNotificationConfig fields as @Nullable.
    • Updated the build() method to reflect nullable fields.
  • spec/src/main/java/io/a2a/spec/MessageSendParams.java
    • Added JSpecify's @nullable import.
    • Annotated configuration and metadata fields as @Nullable.
    • Used Assert.checkNotNullParam for message in the build() method.
  • spec/src/main/java/io/a2a/spec/MethodNotFoundError.java
    • Added JSpecify's @nullable import.
    • Updated the constructor to accept @Nullable code, message, and data parameters.
  • spec/src/main/java/io/a2a/spec/OAuth2SecurityScheme.java
    • Added JSpecify's @nullable import.
    • Annotated description and oauth2MetadataUrl fields as @Nullable.
    • Updated the builder to use Assert.checkNotNullParam for flows.
  • spec/src/main/java/io/a2a/spec/OAuthFlows.java
    • Added JSpecify's @nullable import.
    • Annotated all OAuth flow fields (authorizationCode, clientCredentials, deviceCode) as @Nullable.
  • spec/src/main/java/io/a2a/spec/OpenIdConnectSecurityScheme.java
    • Added JSpecify's @nullable import.
    • Annotated the description field as @Nullable.
    • Updated the builder to use Assert.checkNotNullParam for openIdConnectUrl.
  • spec/src/main/java/io/a2a/spec/PushNotificationConfig.java
    • Added JSpecify's @nullable import.
    • Annotated token, authentication, and id fields as @Nullable.
    • Updated the builder to use Assert.checkNotNullParam for url.
  • spec/src/main/java/io/a2a/spec/PushNotificationNotSupportedError.java
    • Added JSpecify's @nullable import.
    • Updated the constructor to accept @Nullable code, message, and data parameters.
  • spec/src/main/java/io/a2a/spec/Task.java
    • Added JSpecify's @nullable import.
    • Annotated several fields (artifacts, history, metadata) as @Nullable.
    • Used Assert.checkNotNullParam for id, contextId, and status in the build() method.
  • spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java
    • Added JSpecify's @nullable import.
    • Annotated append, lastChunk, and metadata fields as @Nullable.
    • Used Assert.checkNotNullParam for taskId, artifact, and contextId in the build() method.
  • spec/src/main/java/io/a2a/spec/TaskNotCancelableError.java
    • Added JSpecify's @nullable import.
    • Updated the constructor to accept @Nullable code, message, and data parameters.
  • spec/src/main/java/io/a2a/spec/TaskNotFoundError.java
    • Added JSpecify's @nullable import.
    • Updated the constructor to accept @Nullable code, message, and data parameters.
  • spec/src/main/java/io/a2a/spec/TaskStatus.java
    • Added JSpecify's @nullable import.
    • Annotated the message field as @Nullable.
    • Refactored the compact constructor to explicitly assign fields and handle nullable timestamp.
    • Simplified the single-state constructor to use OffsetDateTime.now(ZoneOffset.UTC) directly.
  • spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java
    • Added JSpecify's @nullable import.
    • Annotated the metadata field as @Nullable.
    • Used Assert.checkNotNullParam for taskId, status, and contextId in the build() method.
  • spec/src/main/java/io/a2a/spec/UnsupportedOperationError.java
    • Added JSpecify's @nullable import.
    • Updated the constructor to accept @Nullable code, message, and data parameters.
  • spec/src/main/java/io/a2a/spec/VersionNotSupportedError.java
    • Added JSpecify's @nullable import.
    • Updated the constructor to accept @Nullable code, message, and data parameters.
  • spec/src/main/java/io/a2a/spec/package-info.java
    • Added @org.jspecify.annotations.NullMarked annotation to the package for null-safety enforcement.
  • transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java
    • Added a defaultConfiguration helper method to create MessageSendConfiguration instances.
    • Updated MessageSendParams builders in test cases to include a default configuration.
  • transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java
    • Changed the configId parameter in getTaskPushNotificationConfiguration to be non-nullable.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request successfully migrates the spec module to JSpecify annotations for null-safety, aligning with the stated objectives. Key changes include applying @Nullable annotations to optional fields and parameters, moving builder validation to build() methods using Assert.checkNotNullParam(), and introducing necessary null checks in various parts of the codebase. The test updates reflect these null-safety changes, ensuring continued correctness. The simplification of REST route handling for push notification configurations is also a positive improvement.

Copy link
Collaborator

@kabir kabir left a comment

Choose a reason for hiding this comment

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

One small question

this.defaultInputModes = card.defaultInputModes != null ? new ArrayList<>(card.defaultInputModes) : null;
this.defaultOutputModes = card.defaultOutputModes != null ? new ArrayList<>(card.defaultOutputModes) : null;
this.skills = card.skills != null ? new ArrayList<>(card.skills) : null;
this.defaultInputModes = card.defaultInputModes != null ? new ArrayList<>(card.defaultInputModes) : Collections.emptyList();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should we use null still if the original was null?
Does emptyList() instead of null change the behaviour?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I don't think it would have a big impact

this.skills = card.skills != null ? new ArrayList<>(card.skills) : null;
this.defaultInputModes = card.defaultInputModes != null ? new ArrayList<>(card.defaultInputModes) : Collections.emptyList();
this.defaultOutputModes = card.defaultOutputModes != null ? new ArrayList<>(card.defaultOutputModes) : Collections.emptyList();
this.skills = card.skills != null ? new ArrayList<>(card.skills) : Collections.emptyList();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same null vs emptyList() questions

- Add @NullMarked to io.a2a.spec package
- Apply @nullable annotations to optional fields and parameters
- Move builder validation to build() methods using Assert.checkNotNullParam()
- Fix null-safety issues in RequestContext ID generation
- Add null checks for push notification configuration
- Simplify REST route handling for push notification configs
- Update tests to provide required MessageSendConfiguration

Signed-off-by: Emmanuel Hugonnet <ehugonne@redhat.com>
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.

2 participants