Summary
Running autolith auth <provider> for a custom OpenAI-compatible provider displays the hidden API-key prompt, but then immediately exits without allowing any input:
╭─ local-vllm authentication
│ Paste the local-vllm API key below, then press Enter.
│ Input is hidden. Nothing will appear while you type or paste.
╰─ API key › Autolith could not start: Could not identify the input descriptor for API-key entry; no key was read.
The same failure occurs with both the Nix invocation and a normally installed Autolith binary.
The failure happens before the API key is read or any request is made to the provider.
Environment
Minimal provider configuration
(in-package #:autolith)
(register-openai-compatible-provider
:name "local-vllm"
:description "Local vLLM provider"
:endpoint "http://localhost:8080/v1/chat/completions"
:models '("qwen3.8-27b"))
Steps to reproduce
-
Add the provider configuration above to:
~/.config/autolith/init.lisp
-
Run:
-
Attempt to enter any API key, including a dummy value such as:
Actual behavior
Autolith prints the API-key prompt and immediately exits with:
Autolith could not start: Could not identify the input descriptor for API-key entry; no key was read.
No input is read and no credential is saved.
Expected behavior
Autolith should disable terminal echo, read the API key from stdin, restore the terminal mode, and save the credential.
For local OpenAI-compatible servers that do not require authentication, users should still be able to enter a nonempty dummy credential such as EMPTY.
Apparent cause
The relevant call chain is:
main-authenticate
-> provider-authenticate
-> openai-compatible--authenticate
-> api-key-read-hidden
-> api-key--hidden-input-mode
-> api-key--input-file-descriptor
openai-compatible--authenticate currently calls the hidden-input helper approximately as follows:
(api-key-read-hidden
provider-name
:stream (or stream *standard-output*))
It does not supply either :input or :input-file-descriptor.
The hidden-input implementation attempts to determine the descriptor with:
(or configured
(ignore-errors
(sb-sys:fd-stream-fd input)))
When *standard-input* is represented by a wrapper stream rather than a direct SBCL fd-stream, sb-sys:fd-stream-fd fails. Because the error is ignored, the descriptor becomes NIL, after which api-key--hidden-input-mode deliberately signals:
Could not identify the input descriptor for API-key entry; no key was read.
This happens before the subsequent read-line call.
The fail-closed behavior of api-key-read-hidden appears intentional and is covered by tests for descriptorless wrapper streams. The problem is that the CLI authentication path does not provide the known stdin descriptor to the helper.
Confirmed workaround
Adding the following override to init.lisp makes authentication work:
(defun api-key--input-file-descriptor (input configured)
(or configured
(ignore-errors
(sb-sys:fd-stream-fd input))
0))
After applying this workaround, the same command accepts the hidden input and stores the API key successfully:
This confirms that descriptor discovery is the failing point.
The workaround is suitable for the CLI case, but probably should not be adopted unchanged upstream because it maps every descriptorless input stream to file descriptor 0, even when the supplied stream is not actually stdin.
Suggested fix
Minimal targeted fix
Because the OpenAI-compatible authenticator reads from the process’s standard input, it could explicitly pass the POSIX stdin descriptor:
(let ((api-key
(api-key-read-hidden
provider-name
+ :input *standard-input*
+ :input-file-descriptor 0
:stream (or stream *standard-output*))))
This preserves the hidden-input helper’s fail-closed behavior for arbitrary programmatic streams while making the CLI authentication path reliable.
More general fix
A more complete solution would propagate input information through the provider authentication interface:
-
Extend provider-authenticate and registered authenticator functions to accept:
:input
:input-file-descriptor
-
Have main-authenticate pass:
:input *standard-input*
:input-file-descriptor 0
-
Forward those values through OpenAI-compatible and other API-key authentication implementations to api-key-read-hidden.
That would avoid hard-coding stdin knowledge inside individual providers and would make the existing explicit-descriptor design work consistently.
Suggested regression test
Add a test for the OpenAI-compatible authentication path that replaces api-key-read-hidden, invokes provider-authenticate, and verifies that the known stdin descriptor is forwarded:
(let ((observed-descriptor nil))
(test-call-with-function-replacements
(list
(list 'api-key-read-hidden
(lambda (provider-name
&key input input-file-descriptor stream note)
(declare (ignore provider-name input stream note))
(setf observed-descriptor input-file-descriptor)
"test-key")))
(lambda ()
(provider-authenticate provider
:stream (make-string-output-stream)
:open-browser-p nil)))
(test-assert
(eql observed-descriptor 0)
"OpenAI-compatible authentication supplies the stdin descriptor"))
The existing lower-level tests already verify that:
- descriptorless wrappers fail closed;
- an explicitly supplied descriptor permits hidden input;
- terminal echo is restored after successful or failed reads.
The missing coverage is that the real OpenAI-compatible CLI authentication path supplies that descriptor.
Summary
Running
autolith auth <provider>for a custom OpenAI-compatible provider displays the hidden API-key prompt, but then immediately exits without allowing any input:The same failure occurs with both the Nix invocation and a normally installed Autolith binary.
The failure happens before the API key is read or any request is made to the provider.
Environment
Autolith: current master /
0.33.8OS: Linux x86_64
Provider: custom OpenAI-compatible provider registered from
init.lispReproduced with:
and:
Minimal provider configuration
Steps to reproduce
Add the provider configuration above to:
Run:
Attempt to enter any API key, including a dummy value such as:
Actual behavior
Autolith prints the API-key prompt and immediately exits with:
No input is read and no credential is saved.
Expected behavior
Autolith should disable terminal echo, read the API key from stdin, restore the terminal mode, and save the credential.
For local OpenAI-compatible servers that do not require authentication, users should still be able to enter a nonempty dummy credential such as
EMPTY.Apparent cause
The relevant call chain is:
openai-compatible--authenticatecurrently calls the hidden-input helper approximately as follows:It does not supply either
:inputor:input-file-descriptor.The hidden-input implementation attempts to determine the descriptor with:
When
*standard-input*is represented by a wrapper stream rather than a direct SBCLfd-stream,sb-sys:fd-stream-fdfails. Because the error is ignored, the descriptor becomesNIL, after whichapi-key--hidden-input-modedeliberately signals:This happens before the subsequent
read-linecall.The fail-closed behavior of
api-key-read-hiddenappears intentional and is covered by tests for descriptorless wrapper streams. The problem is that the CLI authentication path does not provide the known stdin descriptor to the helper.Confirmed workaround
Adding the following override to
init.lispmakes authentication work:After applying this workaround, the same command accepts the hidden input and stores the API key successfully:
This confirms that descriptor discovery is the failing point.
The workaround is suitable for the CLI case, but probably should not be adopted unchanged upstream because it maps every descriptorless input stream to file descriptor
0, even when the supplied stream is not actually stdin.Suggested fix
Minimal targeted fix
Because the OpenAI-compatible authenticator reads from the process’s standard input, it could explicitly pass the POSIX stdin descriptor:
(let ((api-key (api-key-read-hidden provider-name + :input *standard-input* + :input-file-descriptor 0 :stream (or stream *standard-output*))))This preserves the hidden-input helper’s fail-closed behavior for arbitrary programmatic streams while making the CLI authentication path reliable.
More general fix
A more complete solution would propagate input information through the provider authentication interface:
Extend
provider-authenticateand registered authenticator functions to accept:Have
main-authenticatepass:Forward those values through OpenAI-compatible and other API-key authentication implementations to
api-key-read-hidden.That would avoid hard-coding stdin knowledge inside individual providers and would make the existing explicit-descriptor design work consistently.
Suggested regression test
Add a test for the OpenAI-compatible authentication path that replaces
api-key-read-hidden, invokesprovider-authenticate, and verifies that the known stdin descriptor is forwarded:The existing lower-level tests already verify that:
The missing coverage is that the real OpenAI-compatible CLI authentication path supplies that descriptor.