{"record":{"id":"21d7c049c2131d39","repo":"aaif-goose/goose","slug":"goose-configure-requires-an-interactive-terminal","errorCode":null,"errorMessage":"goose configure requires an interactive terminal.\nIf you installed via 'curl ... | bash', run 'goose configure' separately after installation.","messagePattern":"goose configure requires an interactive terminal\\.\nIf you installed via 'curl \\.\\.\\. \\| bash', run 'goose configure' separately after installation\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/goose-cli/src/commands/configure.rs","lineNumber":147,"sourceCode":"                unreachable!(\"Search entry is not added to the results list\")\n            }\n        }\n    }\n}\n\nstruct CursorRestoreGuard;\n\nimpl Drop for CursorRestoreGuard {\n    fn drop(&mut self) {\n        let mut stdout = std::io::stdout().lock();\n        let _ = stdout.write_all(SHOW_CURSOR);\n        let _ = stdout.flush();\n    }\n}\n\npub async fn handle_configure() -> anyhow::Result<()> {\n    if !std::io::stdin().is_terminal() {\n        anyhow::bail!(\n            \"goose configure requires an interactive terminal.\\n\\\n             If you installed via 'curl ... | bash', run 'goose configure' separately after installation.\"\n        );\n    }\n\n    let _cursor_restore = CursorRestoreGuard;\n    let config = Config::global();\n\n    if !config.exists() {\n        handle_first_time_setup(config).await\n    } else {\n        handle_existing_config().await\n    }\n}\n\n#[cfg(feature = \"telemetry\")]\npub fn configure_telemetry_consent_dialog() -> anyhow::Result<bool> {\n    let config = Config::global();","sourceCodeStart":129,"sourceCodeEnd":165,"githubUrl":"https://github.com/aaif-goose/goose/blob/3810898a7447ec3299be72e223d3570a7aabf0ab/crates/goose-cli/src/commands/configure.rs#L129-L165","documentation":"`goose configure` is a fully interactive wizard (cliclack prompts, cursor control). It checks `stdin().is_terminal()` up front and refuses to run when stdin is a pipe or file — exactly the condition produced by one-line installers like `curl ... | bash` that try to launch the wizard inline.","triggerScenarios":"`curl -sSL https://... | bash` where the piped script calls `goose configure`; `goose configure < /dev/null` in CI; running configure over SSH with no TTY or from a non-interactive service context.","commonSituations":"One-line installer scripts; Docker/CI containers; scheduled tasks; `su`/`sudo` invocations that detach the TTY.","solutions":["Run `goose configure` as a separate step in a real terminal after installation","For automation, write ~/.config/goose/config.yaml and export env vars directly instead of using the wizard","Wrap in a pseudo-TTY when unavoidable: `script -qec 'goose configure' /dev/null`"],"exampleFix":"# before\ncurl -sSL https://... | bash    # installer runs configure inside the pipe -> bails\n\n# after\ncurl -sSL https://... | bash\n# then, in your terminal:\ngoose configure","handlingStrategy":"validation","validationCode":"# Only attempt the wizard when stdin is a TTY\nif [ -t 0 ]; then\n  goose configure\nelse\n  echo 'configure skipped: no interactive terminal' >&2\nfi","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never launch interactive wizards from piped installers","Provision config via file + env vars in automation","Use pseudo-TTY wrappers (script/tmux) only as a deliberate choice"],"tags":["cli","tty","interactive","installation","configuration"],"backgroundTag":null,"analyzedSha":"3810898a7447ec3299be72e223d3570a7aabf0ab","analyzedAt":"2026-08-16T10:14:26.282Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}