skills-experimental/error-classification-pattern/SKILL.md
# Error Classification Pattern Skill Error Classification Pattern - isAbortError multiple checks + instanceof + Error.name === 'AbortError' + getErrnoCode + isENOENT + isFsInaccessible + classifyAxiosError + kind bucket + status optional + TelemetrySafeError + shortErrorStack + errorMessage + toError。 ## 功能概述 从Claude Code的utils/errors.ts提取的Error classification模式,用于OpenClaw的错误分类处理。 ## 核心机制 ### isAbortError Multiple Checks ```typescript export function isAbortError(e: unknown): boolean { r
npx skillsauth add bianhaifeng789-hue/openclaw-config skills-experimental/error-classification-patternInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Error Classification Pattern - isAbortError multiple checks + instanceof + Error.name === 'AbortError' + getErrnoCode + isENOENT + isFsInaccessible + classifyAxiosError + kind bucket + status optional + TelemetrySafeError + shortErrorStack + errorMessage + toError。
从Claude Code的utils/errors.ts提取的Error classification模式,用于OpenClaw的错误分类处理。
export function isAbortError(e: unknown): boolean {
return (
e instanceof AbortError ||
e instanceof APIUserAbortError ||
(e instanceof Error && e.name === 'AbortError')
)
}
// Multiple abort error checks
# instanceof AbortError (our class)
# instanceof APIUserAbortError (SDK)
# Error.name === 'AbortError' (DOMException)
// The SDK class is checked via instanceof because minified builds mangle class names —
// constructor.name becomes something like 'nJT' and the SDK never sets this.name,
// so string matching silently fails in production.
e instanceof APIUserAbortError
// instanceof for minified builds
# constructor.name mangled in production
# string matching fails
# instanceof reliable
export function getErrnoCode(e: unknown): string | undefined {
if (e && typeof e === 'object' && 'code' in e && typeof e.code === 'string') {
return e.code
}
return undefined
}
// Extract errno code (ENOENT, EACCES, etc.)
# Replaces (e as NodeJS.ErrnoException).code cast
export function isENOENT(e: unknown): boolean {
return getErrnoCode(e) === 'ENOENT'
}
// Check ENOENT (file/dir does not exist)
# Replaces (e as NodeJS.ErrnoException).code === 'ENOENT'
export function isFsInaccessible(e: unknown): e is NodeJS.ErrnoException {
const code = getErrnoCode(e)
return (
code === 'ENOENT' || // path does not exist
code === 'EACCES' || // permission denied
code === 'EPERM' || // operation not permitted
code === 'ENOTDIR' || // path component is not a directory
code === 'ELOOP' // too many symlink levels
)
}
// True if path missing/inaccessible/unreachable
# Expected vs unexpected errors
export function classifyAxiosError(e: unknown): {
kind: AxiosErrorKind
status?: number
message: string
} {
const message = errorMessage(e)
if (!e || typeof e !== 'object' || !('isAxiosError' in e) || !e.isAxiosError) {
return { kind: 'other', message }
}
const err = e as { response?: { status?: number }; code?: string }
const status = err.response?.status
if (status === 401 || status === 403) return { kind: 'auth', status, message }
if (err.code === 'ECONNABORTED') return { kind: 'timeout', status, message }
if (err.code === 'ECONNREFUSED' || err.code === 'ENOTFOUND') {
return { kind: 'network', status, message }
}
return { kind: 'http', status, message }
}
// Classify axios error into buckets
# auth (401/403)
# timeout (ECONNABORTED)
# network (ECONNREFUSED/ENOTFOUND)
# http (other)
# other (not axios)
type AxiosErrorKind =
| 'auth' // 401/403 — caller typically sets skipRetry
| 'timeout' // ECONNABORTED
| 'network' // ECONNREFUSED/ENOTFOUND
| 'http' // other axios error (may have status)
| 'other' // not an axios error
// Bucket classification
# Different handling per kind
status?: number
// Optional status code
# May or may not have status
# e.g., network errors have no status
export class TelemetrySafeError_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS extends Error {
readonly telemetryMessage: string
constructor(message: string, telemetryMessage?: string) {
super(message)
this.name = 'TelemetrySafeError'
this.telemetryMessage = telemetryMessage ?? message
}
}
// Safe to log to telemetry
# Long name confirms verification
# Separate telemetry message
# No sensitive data (file paths, URLs, code)
export function shortErrorStack(e: unknown, maxFrames = 5): string {
if (!(e instanceof Error)) return String(e)
if (!e.stack) return e.message
const lines = e.stack.split('\n')
const header = lines[0] ?? e.message
const frames = lines.slice(1).filter(l => l.trim().startsWith('at '))
if (frames.length <= maxFrames) return e.stack
return [header, ...frames.slice(0, maxFrames)].join('\n')
}
// Extract message + top N stack frames
# Full stack ~500-2000 chars waste tokens
# Keep maxFrames for model
# Full stack in debug logs
export function errorMessage(e: unknown): string {
return e instanceof Error ? e.message : String(e)
}
// Extract error message
# Use for logging/display
# When you only need message
export function toError(e: unknown): Error {
return e instanceof Error ? e : new Error(String(e))
}
// Normalize to Error instance
# Use at catch-site boundaries
# When you need Error instance
{
"kind": "auth",
"status": 401,
"message": "Unauthorized",
"errno": "ENOENT"
}
instanceof AbortError | instanceof APIUserAbortError | Error.name === 'AbortError' → multiple checks → catch all abort shapes
# 多种abort error检查
# instanceof + name check
# 覆盖所有abort shapes
minified builds → constructor.name = 'nJT' → string matching fails → instanceof reliable → SDK class
# minified builds时constructor.name被mangle
# string matching失败
# instanceof可靠
getErrnoCode(e) → ENOENT/EACCES/EPERM/ENOTDIR/ELOOP → isENOENT/isFsInaccessible → helper functions
# errno helper functions
# 避免手动cast
# 安全的errno check
401/403 → auth | ECONNABORTED → timeout | ECONNREFUSED/ENOTFOUND → network → bucket classification
# axios error bucketing
# 不同kind不同handling
# auth/timeout/network/http/other
full stack ~500-2000 chars → shortErrorStack(e, 5) → header + 5 frames → token efficiency → full stack in debug logs
# shortErrorStack节省tokens
# header + maxFrames
# full stack保留在debug logs
utils/errors.ts (238 lines)business
IAA 日报飞书输出能力。 支持把固定 CSV 模板一键转换成: - 中文运营结论 - 飞书卡片 JSON - 飞书发送载荷 Use when: - 需要把 IAA 日报直接发到飞书 - 需要从 CSV 一键生成运营日报
data-ai
IAA日报分析模型 功能: - 渠道日报自动分析 - 小时级+日级ROI联动判断 - 按地区输出加量/降量/停投建议 - 按产品类型输出阈值 - 自动识别利润区/观察区/止损区 Use when: - 分析每天投放数据 - 生成运营日报结论 - 判断是否加量/降量/停投 - 对比美加澳/日韩表现 Keywords: - 日报模型, 投放日报, 加量, 降量, 停投, ROI日报, 分地区分析
data-ai
IAA固定日报分析模板 功能: - 固定字段模板(可直接贴每天数据) - 自动输出总盘结论 - 自动输出美加澳/日韩结论 - 自动给出加量/降量/停投建议 - 适配文件修复/清理两类产品 Use when: - 需要固定日报格式 - 每天复盘渠道表现 - 给运营团队出统一结论 Keywords: - 固定模板, 日报模板, ROI模板, IAA日报, 运营模板
development
# HyperlinkPool Pattern Skill HyperlinkPool Pattern - HyperlinkPool class + strings array + stringMap + Index 0 no hyperlink + intern(hyperlink) + get(id) + undefined handling + 5-minute reset + OSC8 hyperlink interning。 ## 功能概述 从Claude Code的ink/screen.ts提取的HyperlinkPool模式,用于OpenClaw的OSC8超链接池管理。 ## 核心机制 ### HyperlinkPool Class ```typescript export class HyperlinkPool { private strings: string[] = [''] // Index 0 = no hyperlink private stringMap = new Map<string, number>() // strings