关于 error-messages
供 gh-aw 开发者编写验证错误信息的风格指南,要求说明问题、预期格式和具体修复方式,并附有示例与测试建议。
主要功能
- 01按问题与修复组织错误信息
- 02倡导可执行的措辞
- 03区分验证错误与操作错误
- 04提供 YAML 和代码示例
- 05建议为改进后的信息编写测试
适用场景
- 01编写 gh-aw 验证信息
- 02改进含糊的配置错误
- 03为格式或类型错误补充示例
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ github/ gh-aw --skill error-messages安装后在 Claude Code 中输入/error-messages确认已加载。
附带文件ssl.json
详细介绍
错误消息风格指南
gh-aw 验证错误请使用此格式。消息应清晰、可操作,并提供示例。
错误消息模板
[what's wrong]. [what's expected]. [example of correct usage]
确保每条错误消息都回答以下三个问题:
- 哪里出了问题? - 清楚说明验证错误
- 预期是什么? - 说明有效格式或取值
- 如何修复? - 提供正确用法的具体示例
建设性措辞
避免单独使用否定措辞。应搭配预期行为和具体修复方法。
| 避免仅使用否定措辞 | 建议使用的建设性措辞 |
|---|---|
invalid | expected + 有效格式/选项 |
cannot | requires + 前置条件 |
must | should + 示例 |
failed | 操作上下文 + 恢复步骤 |
❌ invalid repo format: %s
✅ invalid repo format '%s' — expected 'owner/repo' format (for example: 'github/gh-aw')
❌ not in a git repository
✅ not in a git repository — run 'git init' or 'cd' to a git repository
何时使用 NewValidationError,何时使用 fmt.Errorf
- 在
*_validation.go逻辑中使用NewValidationError(field, value, reason, suggestion)。field用于指定确切的配置路径reason用于说明失败原因suggestion用于提供包含示例的可操作修复方法
- 对于操作性错误/包装错误(
%w),使用fmt.Errorf,以便在传播底层错误时补充上下文。 - 避免使用
fmt.Errorf("failed to X: %w", err)这类通用包装,除非同时提供恢复指导。
建议文本检查清单
每条 suggestion 都应:
- 说明要更改什么
- 包含最简有效的 YAML/代码示例
- 可能存在歧义时使用 ✓/✗ 标记
示例:
Use one supported engine.
✓ Example:
engine: copilot
✗ Avoid:
engine: unknown
YAML 示例指南
- 示例应简洁且为有效 YAML
- 使用真实的 frontmatter 字段名称
- 仅在 YAML 要求时加引号
- 优先使用 2 个空格缩进
好的示例
这些示例遵循模板,并提供了可操作的指导:
时间增量验证(来自 time_delta.go)
return nil, fmt.Errorf("invalid time delta format: +%s. Expected format like +25h, +3d, +1w, +1mo, +1d12h30m", deltaStr)
✅ 优点:
- 清楚指出无效输入
- 列出多种有效格式示例
- 展示组合格式(+1d12h30m)
带示例的类型验证
return "", fmt.Errorf("manual-approval value must be a string, got %T. Example: manual-approval: \"production\"", val)
✅ 优点:
- 显示实际收到的类型(%T)
- 提供具体的 YAML 示例
- 使用带引号的正确 YAML 语法
带选项的枚举验证
return fmt.Errorf("invalid engine: %s. Valid engines are: copilot, claude, codex, custom. Example: engine: copilot", engineID)
✅ 优点:
- 列出所有有效选项
- 提供最简单的示例
- 使用一致的格式
MCP 配置
return fmt.Errorf("tool '%s' mcp configuration must specify either 'command' or 'container'. Example:\ntools:\n %s:\n command: \"npx @my/tool\"", toolName, toolName)
✅ 优点:
- 说明互斥关系
- 展示真实的工具名称
- 格式化多行 YAML 示例
不好的示例
这些示例不够清晰,也未提供可操作的指导:
过于含糊
return fmt.Errorf("invalid format")
❌ 问题:
- 未指出哪种格式无效
- 未说明预期格式
- 未提供示例
缺少示例
return fmt.Errorf("manual-approval value must be a string")
❌ 问题:
- 说明了要求,但没有示例
- 用户不知道正确的 YAML 语法
- 对收到的类型说明不够清楚
信息不完整
return fmt.Errorf("invalid engine: %s", engineID)
❌ 问题:
- 未列出有效选项
- 未提供修复错误的指导
- 用户必须自行查阅文档
何时应包含示例
以下情况始终应包含示例:
-
格式/语法错误 - 展示正确语法
fmt.Errorf("invalid date format. Expected: YYYY-MM-DD HH:MM:SS. Example: 2024-01-15 14:30:00") -
枚举/选项字段 - 列出所有有效选项
fmt.Errorf("invalid permission level: %s. Valid levels: read, write, none. Example: permissions:\n contents: read", level) -
类型不匹配 - 展示预期类型和示例
fmt.Errorf("timeout-minutes must be an integer, got %T. Example: timeout-minutes: 10", value) -
复杂配置 - 提供完整有效的示例
fmt.Errorf("invalid MCP server config. Example:\nmcp-servers:\n my-server:\n command: \"node\"\n args: [\"server.js\"]")
示例可以省略的情况
以下情况可以省略示例:
-
错误来自包装错误 - 包装其他错误并补充上下文时
return fmt.Errorf("failed to parse configuration: %w", err) -
错误结合上下文后不言自明
return fmt.Errorf("duplicate unit '%s' in time delta: +%s", unit, deltaStr) -
错误指向特定文档
return fmt.Errorf("unsupported feature. See https://docs.example.com/features")
格式指南
动态内容使用类型动词
%s- 字符串%d- 整数%T- 值的类型%v- 通用值%w- 包装错误
多行示例
对于跨多行的 YAML 配置示例:
fmt.Errorf("invalid config. Example:\ntools:\n github:\n mode: \"remote\"")
示例中的引号
在示例中使用正确的 YAML 语法:
// Good - shows quotes when needed
fmt.Errorf("Example: name: \"my-workflow\"")
// Good - shows no quotes for simple values
fmt.Errorf("Example: timeout-minutes: 10")
术语保持一致
使用与 YAML 中相同的字段名称:
// Good - matches YAML field name
fmt.Errorf("timeout-minutes must be positive")
// Bad - uses different name
fmt.Errorf("timeout must be positive")
错误消息测试
所有改进后的错误消息都应有相应的测试:
func TestErrorMessageQuality(t *testing.T) {
err := validateSomething(invalidInput)
require.Error(t, err)
// Error should explain what's wrong
assert.Contains(t, err.Error(), "invalid")
// Error should include expected format or values
assert.Contains(t, err.Error(), "Expected")
// Error should include example
assert.Contains(t, err.Error(), "Example:")
}
迁移策略
改进现有错误消息时:
- 识别错误 - 找出不够明确的验证错误
- 分析上下文 - 了解正在验证的内容
- 应用模板 - 补充错误之处、预期内容和示例
- 添加测试 - 验证错误消息的内容
- 更新注释 - 记录验证逻辑
按类别划分的示例
格式验证
// Time deltas
fmt.Errorf("invalid time delta format: +%s. Expected format like +25h, +3d, +1w, +1mo, +1d12h30m", input)
// Dates
fmt.Errorf("invalid date format: %s. Expected: YYYY-MM-DD or relative like -1w. Example: 2024-01-15 or -7d", input)
// URLs
fmt.Errorf("invalid URL format: %s. Expected: https:// URL. Example: https://api.example.com", input)
类型验证
// Boolean expected
fmt.Errorf("read-only must be a boolean, got %T. Example: read-only: true", value)
// String expected
fmt.Errorf("workflow name must be a string, got %T. Example: name: \"my-workflow\"", value)
// Object expected
fmt.Errorf("permissions must be an object, got %T. Example: permissions:\n contents: read", value)
选项/枚举验证
// Engine selection
fmt.Errorf("invalid engine: %s. Valid engines: copilot, claude, codex, custom. Example: engine: copilot", id)
// Permission levels
fmt.Errorf("invalid permission level: %s. Valid levels: read, write, none. Example: contents: read", level)
// Tool modes
fmt.Errorf("invalid mode: %s. Valid modes: local, remote. Example: mode: \"remote\"", mode)
配置验证
// Missing required field
fmt.Errorf("tool '%s' missing required 'command' field. Example:\ntools:\n %s:\n command: \"node server.js\"", name, name)
// Mutually exclusive fields
fmt.Errorf("cannot specify both 'command' and 'container'. Choose one. Example: command: \"node server.js\"")
// Invalid combination
fmt.Errorf("http MCP servers cannot use 'container' field. Example:\ntools:\n my-http:\n type: http\n url: \"https://api.example.com\"")
参考资料
- 值得参考的优秀示例:
pkg/workflow/time_delta.go - 模式灵感:Go 标准库的错误消息
- 测试示例:
pkg/workflow/*_test.go
工具
编写错误消息时,请考虑:
- 用户的角度(他们需要修复什么?)
- 上下文(错误发生在工作流的哪个环节?)
- 文档(是否应引用特定文档?)
- 复杂程度(是否需要多行示例?)
评论
支持 Markdown