aaif-goose/goose · error

Failed to persist extension state: {}

Error message

Failed to persist extension state: {}

What it means

Agent::remove_extension removes the extension from the running agent successfully, then persists the new enabled-extension list to the session; this error means that persistence step failed (wraps persist_extension_state errors — serialization failure, session not found, or storage I/O). Note the asymmetry: the extension IS removed in memory, but the session metadata on disk may still list it.

Source

Thrown at crates/goose/src/agents/agent.rs:1541

        if extension_name.is_none() {
            if let Some(final_output_tool) = self.final_output_tool.lock().await.as_ref() {
                prefixed_tools.push(final_output_tool.tool());
            }
        }

        prefixed_tools
    }

    pub async fn remove_extension(&self, name: &str, session_id: &str) -> Result<()> {
        self.extension_manager.remove_extension(name).await?;
        self.remove_frontend_extension(name).await;

        // Persist extension state after successful removal
        self.persist_extension_state(session_id)
            .await
            .map_err(|e| {
                error!("Failed to persist extension state: {}", e);
                anyhow!("Failed to persist extension state: {}", e)
            })?;

        Ok(())
    }

    pub async fn list_extensions(&self) -> Vec<String> {
        let mut extensions = self
            .extension_manager
            .list_extensions()
            .await
            .expect("Failed to list extensions");
        extensions.extend(
            self.frontend_extension_configs()
                .await
                .into_iter()
                .map(|config| config.name()),
        );
        extensions

View on GitHub (pinned to 3810898a74)

Solutions

  1. Verify the session_id exists and its storage is writable (check the sessions directory under the goose config dir)
  2. Retry the removal once storage is healthy — note the in-memory removal already succeeded, so a restart without persist will resurrect the extension
  3. If it keeps failing, manually clear the 'enabled_extensions.v0' entry in the session's extension_data
  4. Read the wrapped {e} to distinguish 'session not found' from serialization failure
Defensive patterns

Strategy: try-catch

Validate before calling

// Confirm the session is readable/writable before removing:
if session_manager.get_session(session_id, false).await.is_err() {
    anyhow::bail!("session {session_id} unavailable; refusing to remove extension");
}

Type guard

fn persist_failed(e: &anyhow::Error) -> bool {
    e.to_string().contains("Failed to persist extension state")
}

Try / catch

if let Err(e) = agent.remove_extension(name, session_id).await {
    if persist_failed(&e) {
        // in-memory removal already happened; re-run persistence or accept
        // that a restart may re-enable the extension
    } else {
        return Err(e);
    }
}

Prevention

When it happens

Trigger: persist_extension_state fails because session_manager.get_session(session_id) can't find/read the session, the session store is unwritable, or EnabledExtensionsState serialization fails (see the sibling 'Failed to serialize extension state' error).

Common situations: Session file deleted or moved while goose is running; permissions/disk issues on the sessions directory; session_id passed from a different/older session; serializer/version-skew issues on the extension list.

Related errors


AI-assisted analysis of aaif-goose/goose@3810898a74 (2026-08-16). Data as JSON: /api/errors/46cbf276976f4213. Report an issue: GitHub.