From 298cf57ef2d42b4c199e3f6fbc71e48d2dcd0457 Mon Sep 17 00:00:00 2001 From: w00609601 Date: Fri, 18 Sep 2026 17:23:27 +0800 Subject: [PATCH 1/3] fix(errors): report template misses as not-exist instead of unsupported-language The template.not_found message claimed the language was unsupported even when the templateUri itself did not exist, which misdirected callers. Reword the bilingual message to a plain does-not-exist statement: the language is fixed configuration, so the message no longer echoes it, while the declared fact parameters (template_uri, language) and the error-code contract stay unchanged - facts still carry the language for programmatic diagnosis. Also clarify the ErrorCatalog Javadoc that a message template may render a subset of the declared facts, sync the zh/en API reference error tables, and add ErrorMessagesResourceTest pinning both languages and the retained language fact. Signed-off-by: w00609601 --- .../a2at/sdk/core/exception/ErrorCatalog.java | 12 ++- .../prompt_resources/errors/en-US/errors.json | 2 +- .../prompt_resources/errors/zh-CN/errors.json | 2 +- .../resources/ErrorMessagesResourceTest.java | 73 +++++++++++++++++++ docs/en/API_Reference.md | 2 +- "docs/zh/API\345\217\202\350\200\203.md" | 2 +- 6 files changed, 85 insertions(+), 8 deletions(-) create mode 100644 a2a-t-resources/src/test/java/net/openan/a2at/sdk/resources/ErrorMessagesResourceTest.java diff --git a/a2a-t-core/src/main/java/net/openan/a2at/sdk/core/exception/ErrorCatalog.java b/a2a-t-core/src/main/java/net/openan/a2at/sdk/core/exception/ErrorCatalog.java index f890661a..f091ed8c 100644 --- a/a2a-t-core/src/main/java/net/openan/a2at/sdk/core/exception/ErrorCatalog.java +++ b/a2a-t-core/src/main/java/net/openan/a2at/sdk/core/exception/ErrorCatalog.java @@ -13,9 +13,10 @@ * Closed catalog of the machine-readable error codes exposed by the A2A-T SDK. * *

Each constant carries the layered {@code domain.semantic} code string (for example {@code content.param_missing}), - * the {@link Category} that decides which exception family carries it, and the names of the fact parameters the message - * template for the code renders. Message templates live in {@code prompt_resources/errors/{language}/errors.json}; use - * {@link ErrorMessages} to render them. + * the {@link Category} that decides which exception family carries it, and the names of the fact parameters the code + * declares. A message template may render a subset of the declared facts: {@code template.not_found} keeps + * {@code language} in its facts for programmatic diagnosis while its message only reports the missing template. Message + * templates live in {@code prompt_resources/errors/{language}/errors.json}; use {@link ErrorMessages} to render them. * *

The catalog is closed: codes returned by an LLM step that are not in this catalog are mapped to the per-domain * {@code *.rule_violation} fallback constants by the callers, never surfaced raw. @@ -222,7 +223,10 @@ public static Optional byCode(@NonNull String code) { } /** - * Returns the names of the fact parameters the message template of the code renders. + * Returns the names of the fact parameters the code declares. + * + *

A message template may render a subset of these facts; the remaining ones stay available for programmatic + * diagnosis. * * @return fact parameter names, possibly empty, never null */ diff --git a/a2a-t-resources/src/main/resources/prompt_resources/errors/en-US/errors.json b/a2a-t-resources/src/main/resources/prompt_resources/errors/en-US/errors.json index b90a18d2..17ee93a8 100644 --- a/a2a-t-resources/src/main/resources/prompt_resources/errors/en-US/errors.json +++ b/a2a-t-resources/src/main/resources/prompt_resources/errors/en-US/errors.json @@ -1,5 +1,5 @@ { - "template.not_found": "Template '{template_uri}' does not support language '{language}'; check the template URI and language setting", + "template.not_found": "Template '{template_uri}' does not exist", "template.render_failed": "Failed to render template '{template_uri}': {reason}", "template.load_failed": "Failed to read template resource '{resource_path}'", "slot.schema_not_found": "Template '{template_uri}' is missing its slot schema (language '{language}')", diff --git a/a2a-t-resources/src/main/resources/prompt_resources/errors/zh-CN/errors.json b/a2a-t-resources/src/main/resources/prompt_resources/errors/zh-CN/errors.json index a42f8c7d..90cce849 100644 --- a/a2a-t-resources/src/main/resources/prompt_resources/errors/zh-CN/errors.json +++ b/a2a-t-resources/src/main/resources/prompt_resources/errors/zh-CN/errors.json @@ -1,5 +1,5 @@ { - "template.not_found": "模板「{template_uri}」不支持语言「{language}」,请检查模板标识与语言配置", + "template.not_found": "模板「{template_uri}」不存在", "template.render_failed": "模板「{template_uri}」渲染失败:{reason}", "template.load_failed": "模板资源「{resource_path}」读取失败", "slot.schema_not_found": "模板「{template_uri}」缺少参数定义文件(语言「{language}」)", diff --git a/a2a-t-resources/src/test/java/net/openan/a2at/sdk/resources/ErrorMessagesResourceTest.java b/a2a-t-resources/src/test/java/net/openan/a2at/sdk/resources/ErrorMessagesResourceTest.java new file mode 100644 index 00000000..8b2dbfd6 --- /dev/null +++ b/a2a-t-resources/src/test/java/net/openan/a2at/sdk/resources/ErrorMessagesResourceTest.java @@ -0,0 +1,73 @@ +package net.openan.a2at.sdk.resources; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Map; +import net.openan.a2at.sdk.core.exception.ErrorCatalog; +import net.openan.a2at.sdk.core.exception.ErrorMessages; +import org.junit.jupiter.api.Test; + +/** + * Integration tests for the bundled {@code prompt_resources/errors/{language}/errors.json} resources rendered through + * {@link ErrorMessages}. + * + *

Tests cover the following scenarios: + * + *

+ * + * @since 2026-09 + */ +class ErrorMessagesResourceTest { + + private static final String UNKNOWN_URI = "Task-T/network-layer/does-not-exist/v1"; + + private static final Map FACTS = Map.of("template_uri", UNKNOWN_URI, "language", "fr-FR"); + + /** + * Verifies the bundled Chinese {@code template.not_found} message reports the missing template and does not echo + * the configured language. + * + *

Scenario: Render the catalog entry with a template URI and an unsupported language. Expected result: the + * message states the template does not exist and omits the language. + */ + @Test + void should_reportMissingTemplate_When_renderedInChinese() { + String message = ErrorMessages.render(ErrorCatalog.TEMPLATE_NOT_FOUND, "zh-CN", FACTS); + + assertEquals("模板「" + UNKNOWN_URI + "」不存在", message); + assertFalse(message.contains("fr-FR"), message); + } + + /** + * Verifies the bundled English {@code template.not_found} message reports the missing template and does not echo + * the configured language. + * + *

Scenario: Render the catalog entry with a template URI and an unsupported language. Expected result: the + * message states the template does not exist and omits the language. + */ + @Test + void should_reportMissingTemplate_When_renderedInEnglish() { + String message = ErrorMessages.render(ErrorCatalog.TEMPLATE_NOT_FOUND, "en-US", FACTS); + + assertEquals("Template '" + UNKNOWN_URI + "' does not exist", message); + assertFalse(message.contains("fr-FR"), message); + } + + /** + * Verifies the declared {@code language} fact stays available while the message renders only the template URI. + * + *

Scenario: Inspect the catalog entry and render it in English. Expected result: the entry still declares + * {@code language} for programmatic diagnosis and the rendered message renders only the template URI. + */ + @Test + void should_keepLanguageAsDeclaredFact_When_messageDoesNotRenderIt() { + assertTrue(ErrorCatalog.TEMPLATE_NOT_FOUND.getFactParameters().contains("language")); + assertTrue(ErrorCatalog.TEMPLATE_NOT_FOUND.getFactParameters().contains("template_uri")); + } +} diff --git a/docs/en/API_Reference.md b/docs/en/API_Reference.md index ac7c57b1..2390fe57 100644 --- a/docs/en/API_Reference.md +++ b/docs/en/API_Reference.md @@ -1639,7 +1639,7 @@ result.failure() = | Code | Category | Message (en-US) | Message (zh-CN) | | ---- | -------- | ---------------- | --------------- | -| `template.not_found` | BUSINESS | Template '{template_uri}' does not support language '{language}'; check the template URI and language setting | 模板「{template_uri}」不支持语言「{language}」,请检查模板标识与语言配置 | +| `template.not_found` | BUSINESS | Template '{template_uri}' does not exist | 模板「{template_uri}」不存在 | | `template.render_failed` | BUSINESS | Failed to render template '{template_uri}': {reason} | 模板「{template_uri}」渲染失败:{reason} | | `template.load_failed` | INFRA | Failed to read template resource '{resource_path}' | 模板资源「{resource_path}」读取失败 | | `slot.schema_not_found` | BUSINESS | Template '{template_uri}' is missing its slot schema (language '{language}') | 模板「{template_uri}」缺少参数定义文件(语言「{language}」) | diff --git "a/docs/zh/API\345\217\202\350\200\203.md" "b/docs/zh/API\345\217\202\350\200\203.md" index 6712a7bd..0f298f22 100644 --- "a/docs/zh/API\345\217\202\350\200\203.md" +++ "b/docs/zh/API\345\217\202\350\200\203.md" @@ -1630,7 +1630,7 @@ result.failure() = | 错误码 | 类别 | 消息(zh-CN) | 消息(en-US) | | ----------------------------------------- | -------- | ------------------------------------------------------------ | ------------------------------------------------------------ | -| `template.not_found` | BUSINESS | 模板「{template_uri}」不支持语言「{language}」,请检查模板标识与语言配置 | Template '{template_uri}' does not support language '{language}'; check the template URI and language setting | +| `template.not_found` | BUSINESS | 模板「{template_uri}」不存在 | Template '{template_uri}' does not exist | | `template.render_failed` | BUSINESS | 模板「{template_uri}」渲染失败:{reason} | Failed to render template '{template_uri}': {reason} | | `template.load_failed` | INFRA | 模板资源「{resource_path}」读取失败 | Failed to read template resource '{resource_path}' | | `slot.schema_not_found` | BUSINESS | 模板「{template_uri}」缺少参数定义文件(语言「{language}」) | Template '{template_uri}' is missing its slot schema (language '{language}') | From 75f788b85de143c24fa3a9c44eca66100f886f8e Mon Sep 17 00:00:00 2001 From: w00609601 Date: Fri, 18 Sep 2026 17:34:36 +0800 Subject: [PATCH 2/3] test(server): align template-not-found assertion with the not-exist message LlmBackedPromptMetadataExtractorTest asserted the rendered message echoed the configured language. After the template.not_found message was reworded to a plain does-not-exist, the language stays in the facts only; assert the message omits it and the facts keep it for programmatic diagnosis. Signed-off-by: w00609601 --- .../metadata/LlmBackedPromptMetadataExtractorTest.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/a2a-t-server/src/test/java/net/openan/a2at/sdk/server/metadata/LlmBackedPromptMetadataExtractorTest.java b/a2a-t-server/src/test/java/net/openan/a2at/sdk/server/metadata/LlmBackedPromptMetadataExtractorTest.java index 3e1b8123..de63be6c 100644 --- a/a2a-t-server/src/test/java/net/openan/a2at/sdk/server/metadata/LlmBackedPromptMetadataExtractorTest.java +++ b/a2a-t-server/src/test/java/net/openan/a2at/sdk/server/metadata/LlmBackedPromptMetadataExtractorTest.java @@ -1,6 +1,7 @@ package net.openan.a2at.sdk.server.metadata; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -235,7 +236,11 @@ void extractPropagatesTemplateLoadErrorsAsGenerationFailures() { assertEquals("template.not_found", error.getCode()); assertEquals("generation", error.getStage()); assertTrue(error.getMessage().contains("subscribe-incident"), "message must render the template URI"); - assertTrue(error.getMessage().contains("zh-CN"), "message must render the language"); + assertFalse(error.getMessage().contains("zh-CN"), "message must not echo the configured language"); + assertEquals( + Map.of("template_uri", "subscribe-incident", "language", "zh-CN"), + error.getFacts(), + "facts must keep the language for programmatic diagnosis"); } @Test From 82fa5cddc8d5c60a360b1b05641a387705cbe967 Mon Sep 17 00:00:00 2001 From: w00609601 Date: Sat, 19 Sep 2026 01:41:16 +0800 Subject: [PATCH 3/3] refactor(errors): address review nits on the template.not_found message Three concurrent reviews returned APPROVE with two minor findings: the TEMPLATE_NOT_FOUND constant Javadoc still framed the failure as a language problem, and ErrorMessagesResourceTest carried a tautological third case. Reword the constant Javadoc to describe the trigger (missing for the configured language) and the message (names the URI), and drop the redundant case since the retained facts are already pinned by LlmBackedPromptMetadataExtractorTest. Signed-off-by: w00609601 --- .../a2at/sdk/core/exception/ErrorCatalog.java | 2 +- .../sdk/resources/ErrorMessagesResourceTest.java | 15 --------------- 2 files changed, 1 insertion(+), 16 deletions(-) diff --git a/a2a-t-core/src/main/java/net/openan/a2at/sdk/core/exception/ErrorCatalog.java b/a2a-t-core/src/main/java/net/openan/a2at/sdk/core/exception/ErrorCatalog.java index f091ed8c..ae918c36 100644 --- a/a2a-t-core/src/main/java/net/openan/a2at/sdk/core/exception/ErrorCatalog.java +++ b/a2a-t-core/src/main/java/net/openan/a2at/sdk/core/exception/ErrorCatalog.java @@ -27,7 +27,7 @@ public enum ErrorCatalog { // domain: template (template resources) - /** The referenced template does not exist for the configured language. */ + /** The referenced template is missing for the configured language; the message names the template URI. */ TEMPLATE_NOT_FOUND("template.not_found", Category.BUSINESS, "template_uri", "language"), /** Rendering a template with its slot values failed. */ diff --git a/a2a-t-resources/src/test/java/net/openan/a2at/sdk/resources/ErrorMessagesResourceTest.java b/a2a-t-resources/src/test/java/net/openan/a2at/sdk/resources/ErrorMessagesResourceTest.java index 8b2dbfd6..e5359b50 100644 --- a/a2a-t-resources/src/test/java/net/openan/a2at/sdk/resources/ErrorMessagesResourceTest.java +++ b/a2a-t-resources/src/test/java/net/openan/a2at/sdk/resources/ErrorMessagesResourceTest.java @@ -2,7 +2,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Map; import net.openan.a2at.sdk.core.exception.ErrorCatalog; @@ -17,8 +16,6 @@ * *

    *
  • The {@code template.not_found} message reports the missing template instead of claiming an unsupported language - *
  • The declared {@code language} fact stays available for programmatic diagnosis even when the message does not - * render it *
* * @since 2026-09 @@ -58,16 +55,4 @@ void should_reportMissingTemplate_When_renderedInEnglish() { assertEquals("Template '" + UNKNOWN_URI + "' does not exist", message); assertFalse(message.contains("fr-FR"), message); } - - /** - * Verifies the declared {@code language} fact stays available while the message renders only the template URI. - * - *

Scenario: Inspect the catalog entry and render it in English. Expected result: the entry still declares - * {@code language} for programmatic diagnosis and the rendered message renders only the template URI. - */ - @Test - void should_keepLanguageAsDeclaredFact_When_messageDoesNotRenderIt() { - assertTrue(ErrorCatalog.TEMPLATE_NOT_FOUND.getFactParameters().contains("language")); - assertTrue(ErrorCatalog.TEMPLATE_NOT_FOUND.getFactParameters().contains("template_uri")); - } }