siyuan-note/siyuan · error

Invalid template directory

Error message

Invalid template directory

What it means

DocSaveAsTemplate (/api/template/docSaveAsTemplate) accepts an optional `directory` field that must be a JSON string. If the field is present but is null, or cannot be unmarshalled into a string, decoding fails immediately with 'Invalid template directory'. Unlike databaseMode (which falls back to a sentinel), directory is treated as a hard input error.

Solutions

  1. Send directory as a plain string, e.g. {"directory":"templates"}
  2. Omit the directory field entirely if you do not need a subdirectory
  3. Check the client value for null/undefined before including it in the payload

Example fix

// before
{"id":"20240101120000-abc", "name":"tpl", "overwrite":false, "directory":null}
// after
{"id":"20240101120000-abc", "name":"tpl", "overwrite":false, "directory":"my-templates"}
Defensive patterns

Strategy: validation

Validate before calling

if ('directory' in p && (p.directory === null || typeof p.directory !== 'string')) throw new Error('directory must be a string');

Type guard

const isDirectory = (v) => v === undefined || typeof v === 'string';

Try / catch

const res = await fetchPost('/api/template/docSaveAsTemplate', payload); if (res.code !== 0) console.error(res.msg);

Prevention

When it happens

Trigger: POSTing {"id":"...","name":"...","overwrite":true,"directory":null} or directory as a number/boolean/object, e.g. {"directory":123}.

Common situations: A client serializing an undefined variable to null and including the key anyway; a UI dropdown storing a numeric directory index instead of the string name; copy/paste payloads where the directory was quoted incorrectly.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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

Appendix: source

Thrown at kernel/apicontract/template.go:40

		}
		if request.ID, err = legacyField[string](fields, "id", "String", true); err != nil {
			return request, err
		}
		if request.Name, err = legacyField[string](fields, "name", "String", true); err != nil {
			return request, err
		}
		if request.Overwrite, err = legacyField[bool](fields, "overwrite", "Boolean", true); err != nil {
			return request, err
		}
		request.DatabaseMode = "copy"
		if raw, exists := fields["databaseMode"]; exists {
			if string(raw) == "null" || json.Unmarshal(raw, &request.DatabaseMode) != nil {
				request.DatabaseMode = "invalid"
			}
		}
		if raw, exists := fields["directory"]; exists {
			if string(raw) == "null" || json.Unmarshal(raw, &request.Directory) != nil {
				return request, fmt.Errorf("Invalid template directory")
			}
		}
		return
	}
}

type RenderSprigRequest struct {
	Template string `json:"template"`
}

type TemplateDocumentRequest struct {
	ID string `json:"id"`
}

type TemplateDocumentInfo struct {
	Name        string `json:"name"`
	Directory   string `json:"directory"`
	HasDatabase bool   `json:"hasDatabase"`

View on GitHub (pinned to 9f775e8a12)