{"record":{"id":"eb2296741ade1fde","repo":"larksuite/cli","slug":"invalid-risk-q-must-be-read-write-high-risk-writ","errorCode":null,"errorMessage":"invalid risk %q: must be read|write|high-risk-write","messagePattern":"invalid risk %q: must be read\\|write\\|high-risk-write","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"extension/platform/risk.go","lineNumber":51,"sourceCode":"//   - s == \"\"        → (\"\", nil)            \"not specified\"\n//   - s 在闭合枚举   → (Risk(s), nil)       OK\n//   - s 不在枚举内   → (\"\", error)          invalid\n//\n// The (absent vs invalid) split mirrors the cmdpolicy engine's\n// risk_not_annotated vs risk_invalid reason codes — callers can treat\n// the \"\" + nil case as \"not specified\" without losing the distinction\n// from a typo.\n//\n// Matching is strict: \"Read\" / \"READ\" / \" read \" are all rejected.\n// annotation is developer code, not user input — strict matching is\n// the typo-catch mechanism, not a normalisation opportunity.\nfunc ParseRisk(s string) (Risk, error) {\n\tif s == \"\" {\n\t\treturn \"\", nil\n\t}\n\tr := Risk(s)\n\tif _, ok := riskOrder[r]; !ok {\n\t\treturn \"\", fmt.Errorf(\"invalid risk %q: must be read|write|high-risk-write\", s)\n\t}\n\treturn r, nil\n}\n\n// IsValid reports whether r is one of the three recognised values.\nfunc (r Risk) IsValid() bool {\n\t_, ok := riskOrder[r]\n\treturn ok\n}\n\n// Rank returns the comparable rank of r. ok=false when r is not in the\n// closed taxonomy.\nfunc (r Risk) Rank() (rank int, ok bool) {\n\trank, ok = riskOrder[r]\n\treturn rank, ok\n}\n\n// String returns the underlying string. Useful for yaml/json output","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/extension/platform/risk.go#L33-L69","documentation":"ParseRisk in extension/platform only accepts \"read\", \"write\", or \"high-risk-write\" (or empty, meaning unset), checked against the riskOrder map. Any other string is rejected because Risk is a closed three-level enum used to declare plugin command risk.","triggerScenarios":"Calling ParseRisk with values like \"readonly\", \"rw\", \"dangerous\", \"high\", or capitalized \"Read\".","commonSituations":"Manifests/configs authored with free-form risk labels; migrating from another tool's risk vocabulary; typos such as \"high_risk_write\".","solutions":["Use exactly one of \"read\", \"write\", \"high-risk-write\" (lowercase, hyphenated)","Validate with Risk(s).IsValid() before parsing in a pipeline","Normalize/trim input, ensuring hyphens not underscores","Leave empty if risk should be defaulted later"],"exampleFix":"// before\nr, err := platform.ParseRisk(\"high_risk_write\")\n// after\nr, err := platform.ParseRisk(\"high-risk-write\")","handlingStrategy":"validation","validationCode":"var riskValues = map[string]bool{\"read\": true, \"write\": true, \"high-risk-write\": true}\nfunc validRisk(s string) bool { return s == \"\" || riskValues[s] }\nr, err := platform.ParseRisk(strings.ToLower(strings.TrimSpace(cfg.Risk)))","typeGuard":"func isRisk(s string) bool { switch s { case \"\", \"read\", \"write\", \"high-risk-write\": return true }; return false }","tryCatchPattern":"r, err := platform.ParseRisk(s)\nif err != nil { return fmt.Errorf(\"manifest risk: %w\", err) }","preventionTips":["Use the exact hyphenated literals from the error message in manifests","Normalize risk labels when importing from other tools' vocabulary","Validate risk fields in CI manifest linting","Leave risk empty to let the platform default it"],"tags":["plugin","risk","validation","config"],"backgroundTag":"invalid-enum-value","analyzedSha":"7fd6ef3c07182257ce776cdc5a614e122d5bd4b3","analyzedAt":"2026-09-04T21:17:44.649Z","contentChangedAt":"2026-09-04T21:17:44.649Z","schemaVersion":2},"datasetVersion":"2026-09-12T02:17:10.037Z"}