{"record":{"id":"da63bfd9e3979eb7","repo":"alibaba/open-code-review","slug":"s-produced-multi-line-output-expected-a-single-c","errorCode":null,"errorMessage":"%s produced multi-line output; expected a single credential (pipe through 'head -n1' if your command prints more)","messagePattern":"(.+?) produced multi-line output; expected a single credential \\(pipe through 'head -n1' if your command prints more\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/llm/keycmd.go","lineNumber":110,"sourceCode":"\t\treturn \"\", fmt.Errorf(\"%s produced more than 64KiB of output\", label)\n\t}\n\t// ErrWaitDelay only means an orphaned grandchild still holds the pipe; the\n\t// command itself exited fine and its output is already buffered, so use it\n\t// rather than surfacing an exec-internal error.\n\tif err != nil && !errors.Is(err, exec.ErrWaitDelay) {\n\t\t// Covers non-zero exit and command-not-found (the shell exits non-zero\n\t\t// and prints its not-found message on the child's stderr). ExitError.Stderr\n\t\t// stays nil because we assigned c.Stderr, so no output can leak here.\n\t\treturn \"\", fmt.Errorf(\"%s failed: %w\", label, err)\n\t}\n\n\t// Trim a trailing line break; multi-line output past that is ambiguous and refused.\n\t// ContainsAny (not Contains \"\\n\") so a lone interior CR is caught too: TrimRight\n\t// leaves it, TrimSpace below only strips the edges, and a CR inside a credential\n\t// makes net/http reject the Authorization header with an opaque error.\n\ttrimmed := strings.TrimRight(out.buf.String(), \"\\r\\n\")\n\tif strings.ContainsAny(trimmed, \"\\n\\r\") {\n\t\treturn \"\", fmt.Errorf(\"%s produced multi-line output; expected a single credential (pipe through 'head -n1' if your command prints more)\", label)\n\t}\n\t// Same reason as the line-break check, wider net: httpguts.ValidHeaderFieldValue\n\t// (what net/http enforces) rejects every byte below 0x20 except SP and TAB, plus\n\t// DEL. A NUL or VT smuggled in by e.g. `printf 'sk-a\\0b'` would otherwise reach\n\t// net/http as the opaque `invalid header field value for \"Authorization\"`.\n\t//\n\t// Deliberately before the TrimSpace below, so a trailing control byte is an\n\t// error naming its offset rather than silently stripped: only TAB, SP and the\n\t// line breaks already handled above are things a credential command can\n\t// plausibly append by accident. Offsets are therefore into the pre-TrimSpace\n\t// string, which is what the command actually produced.\n\tfor i := 0; i < len(trimmed); i++ {\n\t\tif b := trimmed[i]; (b < 0x20 && b != '\\t') || b == 0x7f {\n\t\t\treturn \"\", fmt.Errorf(\"%s produced a control byte 0x%02X at offset %d; a credential must not contain control characters\", label, b, i)\n\t\t}\n\t}\n\n\tkey := strings.TrimSpace(trimmed)","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/alibaba/open-code-review/blob/5cf97d0d15cbd41b602513c4be3bfec3cee5bf7f/internal/llm/keycmd.go#L92-L128","documentation":"Credentials must be a single line. After trimming only trailing CR/LF, any remaining newline or carriage return means ambiguous multi-line output, which is refused with this actionable message. Interior CR is caught too, because a CR inside an Authorization header makes net/http fail with an opaque error.","triggerScenarios":"The key command prints the credential plus additional lines — banners, labels (e.g. `echo \"token: abc\"`), pretty-printed JSON, or CRLF line endings from Windows-style output.","commonSituations":"Helper scripts echoing a label before the key; commands printing two values; Windows-produced files with CRLF read by the command; AWS CLI returning a wrapped multi-line value.","solutions":["Pipe the command through head -n1 as the error suggests, or select the credential with jq -r / awk","Fix the helper script to print only the single credential on stdout","Convert CRLF output to LF (dos2unix or tr -d '\\r') if the source emits Windows line endings","Extract the exact field (e.g. --query ... --output text) instead of printing the whole payload"],"exampleFix":"// before\nkeyCmd: \"my-login-tool\"\n// after\nkeyCmd: \"my-login-tool | head -n1\"","handlingStrategy":"validation","validationCode":"out, _ := exec.Command(\"sh\", \"-c\", \"<your-key-cmd>\").Output()\nif strings.ContainsAny(strings.TrimRight(string(out), \"\\r\\n\"), \"\\n\\r\") { /* multi-line; fix command */ }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pipe through head -n1 when the tool prints extra lines","Convert CRLF output (dos2unix, tr -d '\\r') for Windows-sourced output","Emit exactly one line from helper scripts"],"tags":["credentials","exec","output-format"],"backgroundTag":"multiline-credential-output","analyzedSha":"5cf97d0d15cbd41b602513c4be3bfec3cee5bf7f","analyzedAt":"2026-09-02T02:08:09.116Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}