{"record":{"id":"0c944111788c5540","repo":"AlistGo/alist","slug":"the-line-s-is-not-a-multiple-of-2-0c9441","errorCode":null,"errorMessage":"the line '%s' is not a multiple of 2","messagePattern":"the line '(.+?)' is not a multiple of 2","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"drivers/url_tree/util.go","lineNumber":51,"sourceCode":" * url8\n */\n// if there are no name, use the last segment of url as name\nfunc BuildTree(text string, headSize bool) (*Node, error) {\n\tlines := strings.Split(text, \"\\n\")\n\tvar root = &Node{Level: -1, Name: \"root\"}\n\tstack := []*Node{root}\n\tfor _, line := range lines {\n\t\t// calculate indent\n\t\tindent := 0\n\t\tfor i := 0; i < len(line); i++ {\n\t\t\tif line[i] != ' ' {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tindent++\n\t\t}\n\t\t// if indent is not a multiple of 2, it is an error\n\t\tif indent%2 != 0 {\n\t\t\treturn nil, fmt.Errorf(\"the line '%s' is not a multiple of 2\", line)\n\t\t}\n\t\t// calculate level\n\t\tlevel := indent / 2\n\t\tline = strings.TrimSpace(line[indent:])\n\t\t// if the line is empty, skip\n\t\tif line == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\t// if level isn't greater than the level of the top of the stack\n\t\t// it is not the child of the top of the stack\n\t\tfor level <= stack[len(stack)-1].Level {\n\t\t\t// pop the top of the stack\n\t\t\tstack = stack[:len(stack)-1]\n\t\t}\n\t\t// if the line is a folder\n\t\tif isFolder(line) {\n\t\t\t// create a new node\n\t\t\tnode := &Node{","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/drivers/url_tree/util.go#L33-L69","documentation":"The url_tree driver builds a file hierarchy from an indented text structure where each nesting level is exactly two spaces. When a line's leading-space count is odd (indent%2 != 0), the tree parser cannot compute a level and aborts tree construction with this per-line error.","triggerScenarios":"Any line in the url_structure config whose indentation is 1, 3, 5, ... spaces — e.g. a tab converted partially, or manual alignment with an odd number of spaces.","commonSituations":"Copy-pasting tree text from editors with tab expansion, mixing tabs and spaces, or hand-editing the structure and adding a stray space. Fails at storage Init, so the mount is rejected outright.","solutions":["Re-indent the whole url_structure using only multiples of two spaces per level (0, 2, 4, 6 ...).","Replace all tabs with spaces in the config before saving.","Re-generate the structure with StringifyTree output as a canonical reference — it always emits two spaces per level."],"exampleFix":"// before\nroot:\n  a.txt http://x/a\n   b.txt http://x/b   // 3 spaces -> error\n\n// after\nroot:\n  a.txt http://x/a\n  b.txt http://x/b","handlingStrategy":"validation","validationCode":"func validateIndentation(structure string) error {\n    for _, line := range strings.Split(structure, \"\\n\") {\n        indent := 0\n        for indent < len(line) && line[indent] == ' ' { indent++ }\n        if indent%2 != 0 {\n            return fmt.Errorf(\"line %q has odd indent\", line)\n        }\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"if _, err := BuildTree(structure, headSize); err != nil {\n    if strings.Contains(err.Error(), \"not a multiple of 2\") { /* fix indentation in config */ }\n    return err\n}","preventionTips":["Use exactly two spaces per nesting level","Expand tabs to spaces before pasting into url_structure","Round-trip via StringifyTree to normalize formatting"],"tags":["url-tree","indentation","config","validation"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}