{"record":{"id":"efb4b5c561cf6ad2","repo":"chenhg5/cc-connect","slug":"empty-token","errorCode":null,"errorMessage":"empty token","messagePattern":"empty token","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/max/max.go","lineNumber":1215,"sourceCode":"\tif resp.StatusCode != http.StatusOK {\n\t\treturn nil, \"\", fmt.Errorf(\"HTTP %d\", resp.StatusCode)\n\t}\n\tdata, err := io.ReadAll(io.LimitReader(resp.Body, maxAttachmentBytes+1))\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\tif len(data) > maxAttachmentBytes {\n\t\treturn nil, \"\", fmt.Errorf(\"attachment exceeds %d bytes\", maxAttachmentBytes)\n\t}\n\treturn data, resp.Header.Get(\"Content-Type\"), nil\n}\n\n// resolveMediaURL asks MAX for the playable/downloadable URL of a video or\n// audio attachment. MAX delivers only an opaque token in the message payload\n// and exposes /videos/{token} and /audios/{token} for resolution.\nfunc (p *Platform) resolveMediaURL(ctx context.Context, kind, token string) (string, string, error) {\n\tif token == \"\" {\n\t\treturn \"\", \"\", fmt.Errorf(\"empty token\")\n\t}\n\tendpoint := \"/videos/\"\n\tif kind == \"audio\" {\n\t\tendpoint = \"/audios/\"\n\t}\n\treq, err := http.NewRequestWithContext(ctx, http.MethodGet, p.apiBase+endpoint+token, nil)\n\tif err != nil {\n\t\treturn \"\", \"\", err\n\t}\n\tp.setAuth(req)\n\n\tresp, err := p.client.Do(req)\n\tif err != nil {\n\t\treturn \"\", \"\", err\n\t}\n\tdefer resp.Body.Close()\n\tif resp.StatusCode != http.StatusOK {\n\t\tbody, _ := io.ReadAll(io.LimitReader(resp.Body, 512))","sourceCodeStart":1197,"sourceCodeEnd":1233,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/max/max.go#L1197-L1233","documentation":"resolveMediaURL was called with an empty attachment token. MAX delivers only an opaque token in message payloads for video/audio; an empty token means the payload had no resolvable media reference and the /videos/{token} or /audios/{token} call cannot be built.","triggerScenarios":"A video or audio message arrives whose token field is empty/missing and the platform calls resolveMediaURL with that token.","commonSituations":"Media deleted upstream before the bot processed it; MAX schema change renaming the token field; non-media message misrouted into the media resolution path.","solutions":["Validate token != \"\" before calling resolveMediaURL and skip the attachment if empty","Log the raw message payload to confirm the token field name and presence","Check the MAX API version for renamed token fields","Fall back to showing a placeholder in the bridged message"],"exampleFix":"// before\nu, ct, err := p.resolveMediaURL(ctx, \"video\", att.Token)\n// after\nif att.Token == \"\" {\n\treturn nil, \"\", fmt.Errorf(\"max: attachment %s has no media token\", att.ID)\n}\nu, ct, err := p.resolveMediaURL(ctx, \"video\", att.Token)","handlingStrategy":"validation","validationCode":"if token == \"\" { return placeholderForMissingMedia(msg) }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Validate token presence when decoding incoming media messages","Keep field names aligned with the MAX payload schema","Degrade to a placeholder rather than failing the whole message relay"],"tags":["validation","media","attachments"],"backgroundTag":"empty-required-field","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}