{"record":{"id":"202a5aba56a7e9a1","repo":"larksuite/cli","slug":"note-detail-is-empty","errorCode":null,"errorMessage":"note detail is empty","messagePattern":"note detail is empty","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shortcuts/note/note.go","lineNumber":30,"sourceCode":"\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/larksuite/cli/errs\"\n\t\"github.com/larksuite/cli/internal/validate\"\n\t\"github.com/larksuite/cli/shortcuts/common\"\n)\n\n// NoNoteReadPermissionCode is returned when the caller lacks read permission\n// for the requested note.\nconst NoNoteReadPermissionCode = 121005\n\n// ErrEmptyDetail identifies note detail responses that do not contain a note\n// object. Callers should use errors.Is instead of matching the display message.\nvar ErrEmptyDetail = errors.New(\"note detail is empty\")\n\n// artifact_type enum from the note detail API.\nconst (\n\tartifactTypeMainDoc  = 1 // main note document\n\tartifactTypeVerbatim = 2 // verbatim transcript\n)\n\n// note_display_type enum (i32) from the note detail API. Surfaced to callers as\n// a stable string so Agents route on a name, not a magic number.\nconst (\n\tdisplayTypeNormal  = 1\n\tdisplayTypeUnified = 2\n)\n\n// Detail is the parsed note detail shared by `note +detail` and `vc +notes`.\ntype Detail struct {\n\tNoteID           string\n\tCreatorID        string","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/shortcuts/note/note.go#L12-L48","documentation":"ErrEmptyDetail is the sentinel cause identifying a note detail response from GET /open-apis/vc/v1/notes/{note_id} that succeeded at the HTTP/API level but contains no `note` object in the payload. FetchDetail wraps it in a typed errs.InternalError (subtype invalid_response) via WithCause, so callers should match errors.Is(err, ErrEmptyDetail) rather than the display text. It means the API returned an unexpected/invalid response shape, not an auth or transport failure.","triggerScenarios":"Calling shortcuts/note FetchDetail (backing `note +detail` and `vc +notes`) where the decoded JSON response has data[\"note\"] missing, null, or not a JSON object (e.g. an unexpected envelope change or an odd note state the API returns with code 0 but no note payload).","commonSituations":"Fetching a note that exists in an unusual state (deleted/draft/permission-limited) where the backend returns an empty payload with success code; a Lark/Feishu API response-shape change; passing a note_id that resolves but has no retrievable detail.","solutions":["Verify the note_id is correct and the note still exists/opens in the Lark client; retry with a valid note ID.","Check the caller has read permission for the note (permission failures surface via code 121005 / NoNoteReadPermissionCode paths, but exotic permission states can yield empty payloads).","Match errors.Is(err, ErrEmptyDetail) in code and surface a 'note detail unavailable' message instead of retrying blindly.","If it persists on known-good notes, capture the raw response and report a possible API shape change."],"exampleFix":"// before\ndetail, err := note.FetchDetail(ctx, runtime, noteID)\nif err != nil { return err }\n// after\ndetail, err := note.FetchDetail(ctx, runtime, noteID)\nif errors.Is(err, note.ErrEmptyDetail) {\n    return fmt.Errorf(\"note %s has no retrievable detail; check it exists and is shared with you\", noteID)\n} else if err != nil {\n    return err\n}","handlingStrategy":"type-guard","validationCode":"// validate the note_id is a non-empty plausible token before calling\nif noteID == \"\" || strings.ContainsAny(noteID, \"/?# \") {\n    return fmt.Errorf(\"invalid note id %q\", noteID)\n}","typeGuard":"detail, err := note.FetchDetail(ctx, runtime, noteID)\nif err != nil {\n    if errors.Is(err, note.ErrEmptyDetail) { /* invalid-response path */ }\n    return err\n}","tryCatchPattern":"detail, err := note.FetchDetail(ctx, runtime, noteID)\nif errors.Is(err, note.ErrEmptyDetail) {\n    return fmt.Errorf(\"note detail unavailable for %s; verify the note exists and is readable\", noteID)\n}\nif err != nil {\n    return err\n}","preventionTips":["Confirm the note exists and is shared with the calling user before fetching","Distinguish ErrEmptyDetail (bad payload) from API/permission errors before retrying","Pin and update CLI versions so note API shape changes are picked up","Log the raw response when it recurs to report an API shape regression"],"tags":["api-response","invalid-response","notes","sentinel-error"],"backgroundTag":"invalid-api-response-shape","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"}