{"record":{"id":"e39a42924f7dffc3","repo":"tailscale/tailscale","slug":"empty-size","errorCode":null,"errorMessage":"empty size","messagePattern":"empty size","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/tailscale/cli/configure-pve-appliance.go","lineNumber":384,"sourceCode":"\t)\n}\n\n// runQM invokes `qm` with args, streaming its output to Stderr so the\n// caller can see disk-import progress and any error messages.\nfunc runQM(ctx context.Context, args ...string) error {\n\tprintf(\"$ qm %s\\n\", strings.Join(args, \" \"))\n\tcmd := exec.CommandContext(ctx, \"qm\", args...)\n\tcmd.Stdout = Stderr\n\tcmd.Stderr = Stderr\n\treturn cmd.Run()\n}\n\n// parseSizeBytes parses a size string like \"4G\", \"8192M\", \"1024K\", or\n// \"12345\" (bytes) into a byte count. Empty string returns an error.\nfunc parseSizeBytes(s string) (int64, error) {\n\ts = strings.TrimSpace(s)\n\tif s == \"\" {\n\t\treturn 0, errors.New(\"empty size\")\n\t}\n\tmult := int64(1)\n\tswitch last := s[len(s)-1]; {\n\tcase last >= '0' && last <= '9':\n\t\t// no suffix\n\tdefault:\n\t\tswitch last {\n\t\tcase 'K', 'k':\n\t\t\tmult = 1 << 10\n\t\tcase 'M', 'm':\n\t\t\tmult = 1 << 20\n\t\tcase 'G', 'g':\n\t\t\tmult = 1 << 30\n\t\tcase 'T', 't':\n\t\t\tmult = 1 << 40\n\t\tdefault:\n\t\t\treturn 0, fmt.Errorf(\"unknown size suffix %q\", string(last))\n\t\t}","sourceCodeStart":366,"sourceCodeEnd":402,"githubUrl":"https://github.com/tailscale/tailscale/blob/cfe32b8be6a33f8e24fbc369cbfbf7c729d9e042/cmd/tailscale/cli/configure-pve-appliance.go#L366-L402","documentation":"parseSizeBytes converts the --disk-size flag (forms like '4G', '8192M', '1024K', or plain bytes) into a byte count. After strings.TrimSpace, an empty string means no size was supplied, which the parser rejects because the disk image needs a concrete size; the error is returned wrapped by the caller as 'parsing --disk-size: empty size'.","triggerScenarios":"Passing --disk-size= or --disk-size='   ' (whitespace only), or an unset variable expanding to an empty string, so parseSizeBytes gets \"\" at configure-pve-appliance.go:384.","commonSituations":"Shell scripts where $DISK_SIZE is unset/empty but the flag is still emitted; YAML/CI templates with an optional size field left blank; env var name typo producing an empty expansion.","solutions":["Supply a concrete size: `--disk-size=8G` (also accepts M/K or raw bytes)","In scripts, default it first: `--disk-size=\"${DISK_SIZE:-8G}\"` or fail fast with `:?` expansion","Validate the value before invoking: it must be digits optionally followed by K/M/G"],"exampleFix":"# before\n$ sudo tailscale configure pve-appliance --storage=local-lvm --disk-size=\nerror: parsing --disk-size: empty size\n\n# after\n$ sudo tailscale configure pve-appliance --storage=local-lvm --disk-size=8G","handlingStrategy":"validation","validationCode":"DISK_SIZE=\"${DISK_SIZE:-8G}\"\ncase \"$DISK_SIZE\" in\n  ''|*[!0-9KkMmGg]*) echo \"bad --disk-size: '$DISK_SIZE'\"; exit 2 ;;\nesac\ntailscale configure pve-appliance --storage=local-lvm --disk-size=\"$DISK_SIZE\"","typeGuard":null,"tryCatchPattern":"n, err := parseSizeBytes(sizeStr)\nif err != nil {\n    return fmt.Errorf(\"--disk-size=%q invalid (want 4G/512M/bytes): %w\", sizeStr, err)\n}","preventionTips":["Default size variables in scripts instead of passing empty strings","Validate size strings against ^[0-9]+[KkMmGg]?$ before use","Watch for ${VAR} typos that silently expand to empty"],"tags":["cli","proxmox","flag-validation","parsing"],"backgroundTag":null,"analyzedSha":"cfe32b8be6a33f8e24fbc369cbfbf7c729d9e042","analyzedAt":"2026-08-15T19:58:31.583Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}