siyuan-note/siyuan · error

unsupported template database mode [%s]

Error message

unsupported template database mode [%s]

What it means

DocSaveAsTemplateInDirectory saves the current document as a template and decides how embedded attribute views (databases) are handled. databaseMode must be exactly TemplateDatabaseModeCopy (copy) or TemplateDatabaseModeReference (reference); empty defaults to copy. Any other string aborts with this error before any file is written.

Source

Thrown at kernel/model/template.go:271

}

func DocSaveAsTemplate(id, name string, overwrite bool) (code int, err error) {
	return DocSaveAsTemplateWithDatabaseMode(id, name, overwrite, TemplateDatabaseModeCopy)
}

func DocSaveAsTemplateWithDatabaseMode(id, name string, overwrite bool, databaseMode TemplateDatabaseMode) (code int, err error) {
	return DocSaveAsTemplateInDirectory(id, name, "", overwrite, databaseMode)
}

func DocSaveAsTemplateInDirectory(id, name, directory string, overwrite bool, databaseMode TemplateDatabaseMode) (code int, err error) {
	if err = validateTemplateRelativePath(directory, true); err != nil {
		return
	}
	if databaseMode == "" {
		databaseMode = TemplateDatabaseModeCopy
	}
	if TemplateDatabaseModeCopy != databaseMode && TemplateDatabaseModeReference != databaseMode {
		return 0, fmt.Errorf("unsupported template database mode [%s]", databaseMode)
	}

	bt := treenode.GetBlockTree(id)
	if nil == bt {
		return
	}

	tree := prepareExportTree(bt)
	markTemplateAttributeViewModes(tree.Root, databaseMode)
	addBlockIALNodes(tree, true)

	ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
		if !entering {
			return ast.WalkContinue
		}

		// Content in templates is not properly escaped
		// https://github.com/siyuan-note/siyuan/issues/9649

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Use the constants: model.TemplateDatabaseModeCopy or model.TemplateDatabaseModeReference
  2. Pass the exact lowercase strings copy or reference when calling over the HTTP API
  3. Pass an empty string to get the default (copy) behavior instead of inventing a value

Example fix

// before
model.DocSaveAsTemplateWithDatabaseMode(id, dir, 'ref')
// after
model.DocSaveAsTemplateWithDatabaseMode(id, dir, model.TemplateDatabaseModeReference)
Defensive patterns

Strategy: validation

Validate before calling

mode := strings.TrimSpace(databaseMode)
if mode != '' && mode != 'copy' && mode != 'reference' {
    return fmt.Errorf('unsupported template database mode %q', mode)
}

Type guard

func validDBMode(m model.TemplateDatabaseMode) bool {
    return m == model.TemplateDatabaseModeCopy || m == model.TemplateDatabaseModeReference || m == ''
}

Try / catch

id, err := model.DocSaveAsTemplateWithDatabaseMode(id, dir, mode)
if err != nil {
    if strings.Contains(err.Error(), 'unsupported template database mode') {
        return fmt.Errorf('mode must be copy or reference, got %q', mode)
    }
    return err
}

Prevention

When it happens

Trigger: Calling DocSaveAsTemplateInDirectory / DocSaveAsTemplateWithDatabaseMode / the docSaveAsTemplate API with databaseMode set to values like Copy, ref, link, duplicate, or a misspelled variant.

Common situations: API clients guessing mode strings, UI plugins passing translated labels instead of the enum value, or stale code written against an older parameter set.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11). Data as JSON: /api/errors/bdec121b0065e13e. Report an issue: GitHub.