You have a couple issues with your code on the link you provided. As I can't see your complete code, I've made a guess at some parts.
- Double check userState and userData are properly setup. My constructor looks like this:
const DIALOG_STATE_PROPERTY = 'dialogState';
const USER_PROFILE_PROPERTY = 'user';
const EDUCATION_PROMPT = 'education_prompt';
const MAJOR_PROMPT = 'major_prompt';
constructor(conversationState, userState) {
this.conversationState = conversationState;
this.userState = userState;
this.dialogState = this.conversationState.createProperty(DIALOG_STATE_PROPERTY);
this.userData = this.userState.createProperty(USER_PROFILE_PROPERTY);
this.dialogs = new DialogSet(this.dialogState);
// Add prompts that will be used by the main dialogs.
this.dialogs
.add(new TextPrompt(NAME_PROMPT))
.add(new TextPrompt(AGE_PROMPT))
.add(new TextPrompt(GENDER_PROMPT))
.add(new ChoicePrompt(EDUCATION_PROMPT))
.add(new ChoicePrompt(MAJOR_PROMPT));
// Create dialog for prompting user for profile data
this.dialogs.add(new WaterfallDialog(START_DIALOG, [
this.promptForName.bind(this),
this.promptForAge.bind(this),
this.promptForGender.bind(this),
this.promptForEducation.bind(this),
this.promptForMajor.bind(this),
this.returnUser.bind(this)
]));
this.majors = ['English', 'History', 'Computer Science'];
}
Remember that TextPrompt returns the value in step.result. ChoicePrompt returns the value as step.result.value
I'm assuming that in your "promptForEducation" step where you assign the gender value to user that that value is coming from a choice prompt. If not, you will lose the value. Double check you are specifying the correct source.
.add(new TextPrompt(GENDER_PROMPT))
.add(new ChoicePrompt(EDUCATION_PROMPT))
...
if (!user.gender) {
user.gender = step.result;
// Give user object back to UserState storage
await this.userData.set(step.context, user);
console.log(user);
}
- In the "promptForMajor" step, the 2nd parameter in step.Prompt takes a string and represents the dialog portion of the choice. Your code should look like this and will produce the following output. In this instance, I assigned values to "this.majors" in the constructor (shown above).
this.majors = ['English', 'History', 'Computer Science'];
...
if (!user.major) {
// Copy List of majors and add "Other" entry
let majorsOther = this.majors.slice(0, this.majors.length);
majorsOther.push('Einen anderen Studiengang');
// return await step.prompt(MAJOR_PROMPT, this.userData.major, majorsOther);
return await step.prompt(MAJOR_PROMPT, 'List of majors:', majorsOther);
}

- Check that you are saving state at the end of the "onTurn".
// Save changes to the user state.
await this.userState.saveChanges(turnContext);
// End this turn by saving changes to the conversation state.
await this.conversationState.saveChanges(turnContext);
If you implement the above, you should be set. I was able to run with no issues and no loss of state. Additionally, repeatedly not answering the ChoicePrompt before finally providing a proper response did not break state.

Hope of help!