{"record":{"id":"738954a4765e129a","repo":"siyuan-note/siyuan","slug":"dom-is-required","errorCode":null,"errorMessage":"dom is required","messagePattern":"dom is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/apicontract/extension.go","lineNumber":24,"sourceCode":")\n\ntype ExtensionCopyRequest struct {\n\tDOM      string                             `json:\"dom\"`\n\tNotebook *string                            `json:\"notebook\" api:\"optional\"`\n\tHref     *string                            `json:\"href\" api:\"optional\"`\n\tClipType *string                            `json:\"clipType\" api:\"optional\"`\n\tAssets   *string                            `json:\"assets\" api:\"optional\"`\n\tFiles    map[string][]*multipart.FileHeader `json:\"-\"`\n}\n\ntype ExtensionCopyData struct {\n\tMarkdown string `json:\"md\"`\n\tWithMath bool   `json:\"withMath\"`\n}\n\nfunc decodeExtensionCopyForm(form *multipart.Form) (ExtensionCopyRequest, error) {\n\tif len(form.Value[\"dom\"]) == 0 {\n\t\treturn ExtensionCopyRequest{}, fmt.Errorf(\"dom is required\")\n\t}\n\tfirst := func(name string) *string {\n\t\tif values := form.Value[name]; len(values) > 0 {\n\t\t\treturn &values[0]\n\t\t}\n\t\treturn nil\n\t}\n\treturn ExtensionCopyRequest{DOM: form.Value[\"dom\"][0], Notebook: first(\"notebook\"), Href: first(\"href\"),\n\t\tClipType: first(\"clipType\"), Assets: first(\"assets\"), Files: form.File}, nil\n}\n\nfunc extensionCopyRequestSchema() *Schema {\n\tresult := object(map[string]*Schema{\"dom\": {Type: \"string\"}, \"notebook\": {Type: \"string\"},\n\t\t\"href\": {Type: \"string\"}, \"clipType\": {Type: \"string\"}, \"assets\": {Type: \"string\"}}, \"dom\")\n\t// 资源字段名由剪藏页面的 URL 决定；同名上传文件和文本仍保留表单的重复值。\n\tfield := &Schema{AnyOf: []*Schema{{Type: \"string\"}, {Type: \"string\", Format: \"binary\"}}}\n\tresult.AdditionalProperties = &Schema{AnyOf: []*Schema{field, {Type: \"array\", Items: field}}}\n\treturn result","sourceCodeStart":6,"sourceCodeEnd":42,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/apicontract/extension.go#L6-L42","documentation":"decodeExtensionCopyForm parses the multipart form of the extension copy endpoint (web clipper style) and requires a non-empty \"dom\" field; if form.Value[\"dom\"] is empty it returns \"dom is required\". The endpoint copies editor content built from the submitted DOM string, so without it there is nothing to convert to Markdown.","triggerScenarios":"POSTing to the extension copy API via DecodeMultipart without a \"dom\" part in the multipart/form-data body, or with the field named incorrectly (e.g. \"html\", \"content\").","commonSituations":"Browser-extension or scraping integrations building FormData manually and forgetting the dom field; renamed form fields after upstream clipper updates; requests sent as JSON instead of multipart.","solutions":["Add a \"dom\" field to your multipart FormData containing the serialized DOM/content","Verify the field name is exactly \"dom\" and it is sent as a form value part","Use the official siyuan-chrome clipper request format as a reference","Send the request as multipart/form-data, not JSON"],"exampleFix":"// before\nformData.append(\"html\", domString);\n// after\nformData.append(\"dom\", domString);","handlingStrategy":"validation","validationCode":"function assertDomForm(formData) {\n  const dom = formData.get(\"dom\");\n  if (!dom || typeof dom !== \"string\" || dom.length === 0) throw new Error(\"dom is required\");\n}\nassertDomForm(formData);","typeGuard":"const hasDomField = (fd) => typeof fd.get === \"function\" && typeof fd.get(\"dom\") === \"string\" && fd.get(\"dom\").length > 0;","tryCatchPattern":"try { await clipCopy(formData); } catch (e) { if (String(e).includes(\"dom is required\")) console.error(\"Multipart body missing dom field\"); }","preventionTips":["Always append the \"dom\" part to your multipart FormData","Match the official siyuan-chrome clipper field names","Send multipart/form-data, not JSON, to extension endpoints","Log FormData entries in development to verify parts"],"tags":["api","multipart","required-field","extension"],"backgroundTag":"missing-required-argument","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"}