{"record":{"id":"42aef9e191a5be28","repo":"siyuan-note/siyuan","slug":"unsupported-skill-source-content-type-s-expec","errorCode":null,"errorMessage":"unsupported skill source (content-type: %s); expected a zip archive or a SKILL.md text file","messagePattern":"unsupported skill source \\(content-type: (.+?)\\); expected a zip archive or a SKILL\\.md text file","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/util/skill.go","lineNumber":581,"sourceCode":"\tdata, contentType, err := downloadSkillSource(src)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// 按内容类型或来源判定处理方式\n\tisZip := src.isZip || strings.HasPrefix(contentType, \"application/zip\") ||\n\t\tstrings.HasPrefix(contentType, \"application/x-zip-compressed\")\n\n\tif isZip {\n\t\treturn installFromZip(data)\n\t}\n\n\t// 文本：当作单个 SKILL.md\n\tif strings.HasPrefix(contentType, \"text/\") || strings.HasPrefix(strings.TrimSpace(string(data)), \"---\") {\n\t\treturn installFromSingleSkillMD(data)\n\t}\n\n\treturn nil, fmt.Errorf(\"unsupported skill source (content-type: %s); expected a zip archive or a SKILL.md text file\", contentType)\n}\n\n// normalizeSkillURL 把各种输入归一化为下载源\nfunc normalizeSkillURL(raw string) (normalizedSkillSource, error) {\n\traw = strings.TrimSpace(raw)\n\n\t// 1. 整条 \"npx skills add owner/repo ...\" 命令：提取 owner/repo\n\tif strings.Contains(raw, \"skills add\") || strings.Contains(raw, \"skills@\") {\n\t\tif m := skillsAddPattern.FindStringSubmatch(raw); len(m) == 2 {\n\t\t\treturn codeloadSource(m[1], \"main\"), nil\n\t\t}\n\t}\n\n\t// 2. owner/repo 简写（无 scheme、无点、单个 /）\n\tif !strings.Contains(raw, \"://\") && !strings.Contains(raw, \"//\") && ownerRepoPattern.MatchString(raw) {\n\t\treturn codeloadSource(raw, \"main\"), nil\n\t}\n","sourceCodeStart":563,"sourceCodeEnd":599,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/util/skill.go#L563-L599","documentation":"After downloading the skill source, InstallSkill inspects the HTTP Content-Type (and a leading '---' fallback for frontmatter). If the payload is neither a zip archive nor SKILL.md-style text, it refuses to install with this error naming the observed content-type.","triggerScenarios":"Downloading a URL that returns a non-zip, non-text content type — e.g. an HTML error page (text/html is caught as text only if it starts with '---'; HTML pages typically won't), a binary release asset that isn't a zip, or a server returning application/octet-stream for a non-zip file.","commonSituations":"Pointing the installer at a GitHub web page URL instead of the raw file; a release asset that is a tar.gz rather than zip; rate-limit or login pages served with unexpected content types; misconfigured CDN returning wrong MIME type.","solutions":["Use a raw.githubusercontent.com URL for SKILL.md or a direct .zip link instead of an HTML page URL","Ensure release assets are zip archives (not tar.gz or bare binaries)","Check the URL in a browser/curl to see what content-type it actually returns","If the server sends a wrong MIME type but the content is valid SKILL.md text, ensure the body starts with '---'"],"exampleFix":"// before\nInstallSkill(\"https://github.com/owner/repo/blob/main/SKILL.md\") // HTML page\n// after\nInstallSkill(\"https://raw.githubusercontent.com/owner/repo/main/SKILL.md\")","handlingStrategy":"validation","validationCode":"const res = await fetch(url, { method: \"HEAD\" });\nconst ct = res.headers.get(\"content-type\") || \"\";\nconst isZip = ct.includes(\"zip\") || url.endsWith(\".zip\");\nconst isText = ct.startsWith(\"text/\") || url.endsWith(\"SKILL.md\");\nif (!isZip && !isText) throw new Error(`unsupported content-type: ${ct}`);","typeGuard":null,"tryCatchPattern":"try {\n  await installSkill(url);\n} catch (e) {\n  if (String(e.message).startsWith(\"unsupported skill source\")) {\n    // retry with a raw.githubusercontent.com URL or a zip asset\n  }\n}","preventionTips":["Use raw file URLs, not HTML page URLs","Ensure release assets are zip archives","Pre-check content-type with a HEAD request for automation"],"tags":["http","content-type","download"],"backgroundTag":"unsupported-content-type","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}