aaif-goose/goose · error

Extension state serialization failed: {}

Error message

Extension state serialization failed: {}

What it means

Agent::save_extension_state serializes the agent's current extension list as EnabledExtensionsState and writes it into the session's extension_data under key 'enabled_extensions.v0'. This error means serde failed to serialize the Vec<ExtensionConfig> itself (to_extension_data -> to_value), before any storage write happened.

Source

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

        (
            request_id,
            Ok(self.with_post_tool_hook(result, &tool_call, session)),
        )
    }

    /// Save current extension state to session metadata
    /// Should be called after any extension add/remove operation
    pub async fn save_extension_state(&self, session: &SessionConfig) -> Result<()> {
        let extensions_state =
            EnabledExtensionsState::new(self.extension_configs_for_persistence().await);

        let session_manager = self.config.session_manager.clone();
        let mut session_data = session_manager.get_session(&session.id, false).await?;

        if let Err(e) = extensions_state.to_extension_data(&mut session_data.extension_data) {
            warn!("Failed to serialize extension state: {}", e);
            return Err(anyhow!("Extension state serialization failed: {}", e));
        }

        session_manager
            .update(&session.id)
            .extension_data(session_data.extension_data)
            .apply()
            .await?;

        Ok(())
    }

    /// Save current extension state to session by session_id
    pub async fn persist_extension_state(&self, session_id: &str) -> Result<()> {
        let extensions_state =
            EnabledExtensionsState::new(self.extension_configs_for_persistence().await);

        let session_manager = self.config.session_manager.clone();
        let session = session_manager.get_session(session_id, false).await?;

View on GitHub (pinned to 3810898a74)

Solutions

  1. Identify the recently added/changed extension — serialize each ExtensionConfig individually to find the offender
  2. Update goose so the in-memory ExtensionConfig enum and serializer agree
  3. Remove the offending extension and retry the save
  4. If the session's stored state is corrupt, start a fresh session rather than fighting the old one
Defensive patterns

Strategy: try-catch

Validate before calling

// probe-serialize before saving so the failing extension is identifiable:
for ext in agent.extension_configs_for_persistence().await {
    if serde_json::to_value(&ext).is_err() {
        tracing::error!("unserializable extension config: {:?}", ext);
    }
}

Type guard

fn extension_state_serialization_failed(e: &anyhow::Error) -> bool {
    e.to_string().contains("Extension state serialization failed")
}

Try / catch

if let Err(e) = agent.save_extension_state(&session).await {
    if extension_state_serialization_failed(&e) {
        // keep the session running; flag the offending extension for removal
        tracing::error!("extension state not saved: {e}");
    } else {
        return Err(e);
    }
}

Prevention

When it happens

Trigger: An ExtensionConfig variant currently loaded in the agent cannot be represented as JSON — e.g. a serde tag/shape mismatch after a goose upgrade changed the ExtensionConfig enum, or an extension config constructed programmatically with non-serializable content.

Common situations: Upgrading goose while sessions hold extension configs of the old shape and then calling save_extension_state on a code path that still uses old configs; custom-built extensions with unserializable fields; config migrations that leave a legacy variant in memory.

Related errors


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