gravitational/teleport · error
prompt returned invalid credential: %#v
Error message
prompt returned invalid credential: %#v
What it means
After the FIDO2 credential prompt returns a choice, pickAssertion maps it back to the corresponding libfido2 assertion via a credToAssertion map. If the *CredentialInfo returned by prompt.PromptCredential is not a key in that map, the flow aborts with this error. It is a defensive check: a prompt that returns anything other than one of the options it was shown is considered invalid.
Source
Thrown at lib/auth/webauthncli/fido2.go:443
credToAssertion := make(map[*CredentialInfo]*libfido2.Assertion)
for i, assertion := range assertions {
cred := &CredentialInfo{
ID: assertion.CredentialID,
User: UserInfo{
UserHandle: assertion.User.ID,
Name: assertion.User.Name,
},
}
credToAssertion[cred] = assertion
creds[i] = cred
}
chosen, err := prompt.PromptCredential(creds)
if err != nil {
return nil, trace.Wrap(err)
}
assertion, ok := credToAssertion[chosen]
if !ok {
return nil, fmt.Errorf("prompt returned invalid credential: %#v", chosen)
}
return assertion, nil
}
// fido2Register implements FIDO2Register.
func fido2Register(
ctx context.Context,
origin string, cc *wantypes.CredentialCreation, prompt RegisterPrompt,
) (*proto.MFARegisterResponse, error) {
switch {
case origin == "":
return nil, trace.BadParameter("origin required")
case prompt == nil:
return nil, trace.BadParameter("prompt required")
}
if err := cc.Validate(); err != nil {
return nil, trace.Wrap(err)
}View on GitHub (pinned to 1283425b60)
Solutions
- Ensure the custom PromptCredential implementation returns one of the exact *CredentialInfo pointers from its input slice.
- If your prompt round-trips through JSON/UI, map the user's choice back to the original pointer from creds.
- In test stubs, return creds[0] (an element of the input) instead of a newly constructed value.
- Alternatively change lookup to match by credential ID rather than pointer identity (code change in fido2.go).
Example fix
// before (custom prompt returns a copy)
func (p myPrompt) PromptCredential(creds []*CredentialInfo) (*CredentialInfo, error) {
return &CredentialInfo{ID: creds[p.idx].ID}, nil
}
// after
func (p myPrompt) PromptCredential(creds []*CredentialInfo) (*CredentialInfo, error) {
return creds[p.idx], nil
} Defensive patterns
Strategy: type-guard
Validate before calling
chosen, err := prompt.PromptCredential(creds)
if err != nil { return nil, trace.Wrap(err) }
if chosen == nil || !slices.Contains(creds, chosen) {
return nil, errors.New("prompt returned a credential outside the offered set")
} Type guard
func isOfferedCredential(creds []*CredentialInfo, chosen *CredentialInfo) bool {
return chosen != nil && slices.Contains(creds, chosen)
} Try / catch
assertion, err := pickAssertion(ctx, cfg, assertions, user, prompt)
if err != nil {
if strings.Contains(err.Error(), "prompt returned invalid credential") {
return nil, trace.BadParameter("PromptCredential must return one of the pointers it was given")
}
return nil, trace.Wrap(err)
} Prevention
- Prompt implementations must echo back an element of their input slice, never a copy.
- Avoid serializing CredentialInfo through the UI layer (breaks pointer identity).
- Add tests for every custom PromptCredential implementation asserting pointer membership.
When it happens
Trigger: During Login's device-selection/picker path, prompt.PromptCredential returns a *CredentialInfo that was not among the creds slice shown — e.g. a custom Prompt implementation returning a synthesized object, a stale pointer, or a copy.
Common situations: Custom CLI/Web UI prompt implementations (CLICredentialPrompt or bespoke ones) that re-create CredentialInfo instead of returning the passed pointer; test stubs; UI layers that serialize/deserialize the credential and break pointer identity.
Related errors
- you are using a security key that is not registered with Tel
- device already holds a registered credential
- device not registered for passwordless
- device cannot fulfill platform attachment requirement
- device lacks resident key capabilities
AI-assisted analysis of gravitational/teleport@1283425b60 (2026-09-02).
Data as JSON: /api/errors/3d4d22f70d050f66.
Report an issue: GitHub.