siyuan-note/siyuan · error

dom is required

Error message

dom is required

What it means

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.

Solutions

  1. Add a "dom" field to your multipart FormData containing the serialized DOM/content
  2. Verify the field name is exactly "dom" and it is sent as a form value part
  3. Use the official siyuan-chrome clipper request format as a reference
  4. Send the request as multipart/form-data, not JSON

Example fix

// before
formData.append("html", domString);
// after
formData.append("dom", domString);
Defensive patterns

Strategy: validation

Validate before calling

function assertDomForm(formData) {
  const dom = formData.get("dom");
  if (!dom || typeof dom !== "string" || dom.length === 0) throw new Error("dom is required");
}
assertDomForm(formData);

Type guard

const hasDomField = (fd) => typeof fd.get === "function" && typeof fd.get("dom") === "string" && fd.get("dom").length > 0;

Try / catch

try { await clipCopy(formData); } catch (e) { if (String(e).includes("dom is required")) console.error("Multipart body missing dom field"); }

Prevention

When it happens

Trigger: 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").

Common situations: 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.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/738954a4765e129a. Report an issue: GitHub.

Appendix: source

Thrown at kernel/apicontract/extension.go:24

)

type ExtensionCopyRequest struct {
	DOM      string                             `json:"dom"`
	Notebook *string                            `json:"notebook" api:"optional"`
	Href     *string                            `json:"href" api:"optional"`
	ClipType *string                            `json:"clipType" api:"optional"`
	Assets   *string                            `json:"assets" api:"optional"`
	Files    map[string][]*multipart.FileHeader `json:"-"`
}

type ExtensionCopyData struct {
	Markdown string `json:"md"`
	WithMath bool   `json:"withMath"`
}

func decodeExtensionCopyForm(form *multipart.Form) (ExtensionCopyRequest, error) {
	if len(form.Value["dom"]) == 0 {
		return ExtensionCopyRequest{}, fmt.Errorf("dom is required")
	}
	first := func(name string) *string {
		if values := form.Value[name]; len(values) > 0 {
			return &values[0]
		}
		return nil
	}
	return ExtensionCopyRequest{DOM: form.Value["dom"][0], Notebook: first("notebook"), Href: first("href"),
		ClipType: first("clipType"), Assets: first("assets"), Files: form.File}, nil
}

func extensionCopyRequestSchema() *Schema {
	result := object(map[string]*Schema{"dom": {Type: "string"}, "notebook": {Type: "string"},
		"href": {Type: "string"}, "clipType": {Type: "string"}, "assets": {Type: "string"}}, "dom")
	// 资源字段名由剪藏页面的 URL 决定;同名上传文件和文本仍保留表单的重复值。
	field := &Schema{AnyOf: []*Schema{{Type: "string"}, {Type: "string", Format: "binary"}}}
	result.AdditionalProperties = &Schema{AnyOf: []*Schema{field, {Type: "array", Items: field}}}
	return result

View on GitHub (pinned to 9f775e8a12)