siyuan-note/siyuan · error

block write failed: empty block ID

Error message

block write failed: empty block ID

What it means

Returned by writeBlockWriteResult (kernel/cli/cmd/block.go:321), the shared printer invoked by insert/append/prepend after a successful write. It asserts that the model-layer operation carried a non-empty block ID; an empty ID means the write path returned an operation without a usable identifier, so the command refuses to print a meaningless result. This is an internal contract violation between the CLI layer and model.InsertBlock/AppendBlock/PrependBlock, not a flag-usage mistake.

Source

Thrown at kernel/cli/cmd/block.go:321

			ParentID: parentID,
		}
		if err = model.PerformTxSync(&model.Transaction{DoOperations: []*model.Operation{operation}}); err != nil {
			return err
		}
		if bt := treenode.GetBlockTree(parentID); bt != nil {
			model.AppendPushReloadProtyleEntry(bt.RootID)
		}
		return printBlockWriteResult(operation.ID)
	},
}

func printBlockWriteResult(id string) error {
	return writeBlockWriteResult(os.Stdout, id)
}

func writeBlockWriteResult(output io.Writer, id string) error {
	if id == "" {
		return fmt.Errorf("block write failed: empty block ID")
	}
	if outputFormat == "json" {
		data, err := json.MarshalIndent(map[string]string{"id": id}, "", "  ")
		if err != nil {
			return err
		}
		_, err = fmt.Fprintln(output, string(data))
		return err
	}
	_, err := fmt.Fprintln(output, id)
	return err
}

var blockUpdateCmd = &cobra.Command{
	Use:   "update --id <id> [--data <markdown> | --file <path>]",
	Short: "Update block",
	RunE: func(cmd *cobra.Command, args []string) error {
		id, _ := cmd.Flags().GetString("id")

View on GitHub (pinned to afa823b6b4)

Solutions

  1. Check the payload first: ensure --data is non-empty (not just present) or the --file has content — an empty payload can complete the write while producing no block ID
  2. Re-run with --dry-run and echo the exact payload to confirm what is being sent
  3. Inspect the kernel log around the failed command for the underlying transaction result
  4. If the payload is valid and the error persists, capture the exact command plus payload and report it — an empty operation.ID from the model layer indicates a defect worth filing

Example fix

# before: empty-but-present payload passes earlier guards
siyuan-kernel block append --parent 20260605100657-v080a4j --data "$BODY"
# after: refuse empty input before writing
[ -n "$BODY" ] || { echo 'empty body' >&2; exit 2; }
siyuan-kernel block append --parent 20260605100657-v080a4j --data "$BODY"
Defensive patterns

Strategy: try-catch

Validate before calling

# bash: empty-but-present payloads are the main avoidable cause
[ -n "${DATA// /[ ]}" ] || { echo 'refusing to write empty content' >&2; exit 2; }
[ -z "$FILE" ] || [ -s "$FILE" ] || { echo "file empty: $FILE" >&2; exit 2; }
siyuan-kernel block append --parent "$PARENT" --data "$DATA"

Try / catch

In shell: `if ! out=$(siyuan-kernel block append ... 2>&1); then case "$out" in *'empty block ID'*) echo 'write produced no block — check payload and kernel log' >&2; exit 3;; esac; fi` — treat it as a suspected defect: do not retry the same write (risk of duplication on partial success), preserve the command and payload for a bug report.

Prevention

When it happens

Trigger: A write that completes without producing a created block — e.g. empty/whitespace-only --data or --file content that yields no block to insert; an edge case in the model transaction returning a zero-valued operation; a version skew where the operation struct stopped populating ID as the CLI expects.

Common situations: Scripted writes where the data variable is empty but present (so earlier guards pass); feeding an empty file via --file; upgrading the kernel while an older CLI wrapper kept running.

Related errors


AI-assisted analysis of siyuan-note/siyuan@afa823b6b4 (2026-08-18). Data as JSON: /api/errors/9f639d20ebfe36e6. Report an issue: GitHub.