What happened?
Summary
Per the A2A specification (Section 3.1.7, "Create Push Notification Config"), the id field of a push notification config is optional when creating one — the only required input is url, and the server assigns an ID if none is provided. The runtime request paths honor this, but the Java domain record TaskPushNotificationConfig enforces id as non-null, which contradicts the spec, the gRPC proto definition, and the store interface's own documented contract.
Spec reference
What the code does
Consistent with the spec
spec-grpc/src/main/proto/a2a.proto (message TaskPushNotificationConfig, lines 466–480): string id = 2; is not marked (google.api.field_behavior) = REQUIRED; only url is.
- All request entry points (JSON-RPC
JSONRPCUtils.java:224-230, REST RestHandler.java:376-385) parse the body into a proto builder first, so a missing id arrives as the protobuf default empty string "", and InMemoryPushNotificationConfigStore.setInfo (:51) defaults an empty id to the task ID (if (notificationConfig.id().isEmpty()) builder.id(taskId)). So a client omitting id does get a config back with an assigned ID.
Inconsistent with the spec
spec/src/main/java/org/a2aproject/sdk/spec/TaskPushNotificationConfig.java:
- Compact constructor (line 48):
Assert.checkNotNullParam("id", id);
Builder.build() (line 177): Assert.checkNotNullParam("id", id);
This makes id required at the domain-API level, so:
TaskPushNotificationConfig.builder().url(...).build() throws IllegalArgumentException when id is omitted — but the spec says id is optional and the server assigns it.
PushNotificationConfigStore.setInfo documents "If notificationConfig.id() is null or empty, it's set to the task ID" (PushNotificationConfigStore.java:91), but the null branch is dead code — a null id can never be constructed through the public API.
InMemoryPushNotificationConfigStore.setInfo:51 calls notificationConfig.id().isEmpty(); if a null id ever reached it (as the Javadoc promises), it would NPE instead of defaulting.
The current "optional id works at runtime" behavior is load-bearing on the fact that the mapper TaskPushNotificationConfigMapper.fromProto does not apply emptyToNull to id (it does for taskId/token/tenant) — it only survives because proto yields "" rather than null.
Related (secondary) inconsistency
GetTaskPushNotificationConfigParams.java:31-32: the Javadoc describes id as "optional specific configuration ID to retrieve", but the constructor asserts checkNotNullParam("id", id) — so the default config cannot be retrieved without specifying an id.
Expected behavior
id should be optional when constructing/creating a TaskPushNotificationConfig; the server assigns an ID (e.g., defaults to the task ID) when it is absent, and returns the config with the assigned ID.
- Constructing
TaskPushNotificationConfig.builder().url(...).build() should succeed, mirroring the spec and the 0.3 compat record PushNotificationConfig_v0_3 (which only validates url).
Relevant log output
Code of Conduct
What happened?
Summary
Per the A2A specification (Section 3.1.7, "Create Push Notification Config"), the
idfield of a push notification config is optional when creating one — the only required input isurl, and the server assigns an ID if none is provided. The runtime request paths honor this, but the Java domain recordTaskPushNotificationConfigenforcesidas non-null, which contradicts the spec, the gRPC proto definition, and the store interface's own documented contract.Spec reference
idis not required on create; the server generates and returns the assigned ID.What the code does
Consistent with the spec
spec-grpc/src/main/proto/a2a.proto(message TaskPushNotificationConfig, lines 466–480):string id = 2;is not marked(google.api.field_behavior) = REQUIRED; onlyurlis.JSONRPCUtils.java:224-230, RESTRestHandler.java:376-385) parse the body into a proto builder first, so a missingidarrives as the protobuf default empty string"", andInMemoryPushNotificationConfigStore.setInfo(:51) defaults an empty id to the task ID (if (notificationConfig.id().isEmpty()) builder.id(taskId)). So a client omittingiddoes get a config back with an assigned ID.Inconsistent with the spec
spec/src/main/java/org/a2aproject/sdk/spec/TaskPushNotificationConfig.java:Assert.checkNotNullParam("id", id);Builder.build()(line 177):Assert.checkNotNullParam("id", id);This makes
idrequired at the domain-API level, so:TaskPushNotificationConfig.builder().url(...).build()throwsIllegalArgumentExceptionwhenidis omitted — but the spec saysidis optional and the server assigns it.PushNotificationConfigStore.setInfodocuments "IfnotificationConfig.id()is null or empty, it's set to the task ID" (PushNotificationConfigStore.java:91), but the null branch is dead code — a nullidcan never be constructed through the public API.InMemoryPushNotificationConfigStore.setInfo:51callsnotificationConfig.id().isEmpty(); if a nullidever reached it (as the Javadoc promises), it would NPE instead of defaulting.The current "optional id works at runtime" behavior is load-bearing on the fact that the mapper
TaskPushNotificationConfigMapper.fromProtodoes not applyemptyToNulltoid(it does fortaskId/token/tenant) — it only survives because proto yields""rather thannull.Related (secondary) inconsistency
GetTaskPushNotificationConfigParams.java:31-32: the Javadoc describesidas "optional specific configuration ID to retrieve", but the constructor assertscheckNotNullParam("id", id)— so the default config cannot be retrieved without specifying an id.Expected behavior
idshould be optional when constructing/creating aTaskPushNotificationConfig; the server assigns an ID (e.g., defaults to the task ID) when it is absent, and returns the config with the assigned ID.TaskPushNotificationConfig.builder().url(...).build()should succeed, mirroring the spec and the 0.3 compat recordPushNotificationConfig_v0_3(which only validatesurl).Relevant log output
Code of Conduct