diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5b0ba0abe49..d4a8ce30e72 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -28,6 +28,7 @@ Docs: https://docs.openclaw.ai
- Providers/OpenAI Codex: include base `gpt-5.5` and `gpt-5.4` reasoning metadata in the bundled Codex catalog so `/think xhigh` remains available for those models. Fixes #82744.
- Providers/MiniMax: declare CN endpoint auth aliases in the plugin manifest so `minimax-cn` and `minimax-portal-cn` reuse the correct base auth profiles instead of falling back to unrelated models after 401s. Fixes #63823. Thanks @kamusis.
- Agents/model fallback: suppress fallback notices when the active OpenAI Codex runtime reports the same canonical OpenAI model.
+- Agents/music generation: remove model-controlled request timeouts while keeping configured timeouts at a 120-second floor.
- Agents/edit tool: honor `file_path` and related path aliases when resolving edit-recovery targets, so post-write errors no longer surface false edit failures after the file actually changed. Fixes #81909. Thanks @giodl73-repo.
- QQBot: treat only explicit truthy `QQBOT_DEBUG` values as enabling debug logs, so false-like values such as `0` no longer expose debug output. Fixes #82644. (#82697) Thanks @leno23.
- Agents/session_status: resolve implicit no-arg status lookups against the live run session, so `/think` changes report the current thinking level instead of stale sandbox state. Fixes #82669. (#82696) Thanks @leno23.
diff --git a/docs/tools/music-generation.md b/docs/tools/music-generation.md
index 6a3068849b0..eb5afd5f9d0 100644
--- a/docs/tools/music-generation.md
+++ b/docs/tools/music-generation.md
@@ -161,7 +161,6 @@ Direct generation example:
Output format hint when the provider supports it.
Output filename hint.
-Optional provider request timeout in milliseconds. When omitted, OpenClaw uses `agents.defaults.musicGenerationModel.timeoutMs` if configured. Values below 10000ms are raised to 10000ms and reported in the tool result.
Not all providers support all parameters. OpenClaw still validates hard
@@ -173,6 +172,10 @@ them. Tool results report applied settings; `details.normalization`
captures any requested-to-applied mapping.
+Provider request timeouts are operator configuration only. OpenClaw uses
+`agents.defaults.musicGenerationModel.timeoutMs` when configured and raises
+values below 120000ms to 120000ms.
+
## Async behavior
Session-backed music generation runs as a background task:
diff --git a/src/agents/tools/music-generate-tool.test.ts b/src/agents/tools/music-generate-tool.test.ts
index 11940d7c56f..fe4936b0b5a 100644
--- a/src/agents/tools/music-generate-tool.test.ts
+++ b/src/agents/tools/music-generate-tool.test.ts
@@ -421,7 +421,10 @@ describe("createMusicGenerateTool", () => {
config: asConfig({
agents: {
defaults: {
- musicGenerationModel: { primary: "google/lyria-3-clip-preview" },
+ musicGenerationModel: {
+ primary: "google/lyria-3-clip-preview",
+ timeoutMs: 1000,
+ },
},
},
}),
@@ -432,25 +435,24 @@ describe("createMusicGenerateTool", () => {
const result = await tool.execute("call-1", {
prompt: "night-drive synthwave",
- timeoutMs: 1000,
});
const text = (result.content?.[0] as { text: string } | undefined)?.text ?? "";
expect(generateSpy).toHaveBeenCalledTimes(1);
expect(generateMusicOptions().autoProviderFallback).toBe(false);
- expect(generateMusicOptions().timeoutMs).toBe(10_000);
- expect(text).toContain("Timeout normalized: requested 1000ms; used 10000ms.");
+ expect(generateMusicOptions().timeoutMs).toBe(120_000);
+ expect(text).toContain("Timeout normalized: requested 1000ms; used 120000ms.");
const details = detailsOf(result);
- expect(details.timeoutMs).toBe(10_000);
+ expect(details.timeoutMs).toBe(120_000);
expect(details.requestedTimeoutMs).toBe(1000);
expect(details.timeoutNormalization).toEqual({
requested: 1000,
- applied: 10_000,
- minimum: 10_000,
+ applied: 120_000,
+ minimum: 120_000,
});
});
- it("uses configured timeoutMs for music generation and lets calls override it", async () => {
+ it("uses configured timeoutMs for music generation and ignores call-provided timeoutMs", async () => {
vi.spyOn(musicGenerationRuntime, "generateMusic").mockResolvedValue({
provider: "google",
model: "lyria-3-clip-preview",
@@ -492,13 +494,13 @@ describe("createMusicGenerateTool", () => {
});
const overrideResult = await tool.execute("call-timeout-override", {
prompt: "night-drive synthwave",
- timeoutMs: 12_345,
+ timeoutMs: 240_000,
});
expect(generateMusicOptions(0).timeoutMs).toBe(180_000);
- expect(generateMusicOptions(1).timeoutMs).toBe(12_345);
+ expect(generateMusicOptions(1).timeoutMs).toBe(180_000);
expect(detailsOf(defaultResult).timeoutMs).toBe(180_000);
- expect(detailsOf(overrideResult).timeoutMs).toBe(12_345);
+ expect(detailsOf(overrideResult).timeoutMs).toBe(180_000);
});
it("starts background generation and wakes the session with MEDIA lines", async () => {
@@ -543,7 +545,10 @@ describe("createMusicGenerateTool", () => {
config: asConfig({
agents: {
defaults: {
- musicGenerationModel: { primary: "google/lyria-3-clip-preview" },
+ musicGenerationModel: {
+ primary: "google/lyria-3-clip-preview",
+ timeoutMs: 1000,
+ },
},
},
}),
@@ -563,31 +568,30 @@ describe("createMusicGenerateTool", () => {
const result = await tool.execute("call-1", {
prompt: "night-drive synthwave",
instrumental: true,
- timeoutMs: 1000,
});
const text = (result.content?.[0] as { text: string } | undefined)?.text ?? "";
expect(text).toContain("Background task started for music generation (task-123).");
expect(text).toContain("Do not call music_generate again for this request.");
- expect(text).toContain("Timeout normalized: requested 1000ms; used 10000ms.");
+ expect(text).toContain("Timeout normalized: requested 1000ms; used 120000ms.");
const details = detailsOf(result);
expect(details.async).toBe(true);
expect(details.status).toBe("started");
expect((details.task as { taskId?: unknown }).taskId).toBe("task-123");
expect(details.instrumental).toBe(true);
- expect(details.timeoutMs).toBe(10_000);
+ expect(details.timeoutMs).toBe(120_000);
expect(details.requestedTimeoutMs).toBe(1000);
expect(details.timeoutNormalization).toEqual({
requested: 1000,
- applied: 10_000,
- minimum: 10_000,
+ applied: 120_000,
+ minimum: 120_000,
});
if (!scheduledWork) {
throw new Error("expected scheduled music generation work");
}
await scheduledWork();
expect(generateMusicOptions().autoProviderFallback).toBe(false);
- expect(generateMusicOptions().timeoutMs).toBe(10_000);
+ expect(generateMusicOptions().timeoutMs).toBe(120_000);
const progress = taskProgressCall();
expect(String(progress.runId)).toMatch(/^tool:music_generate:/);
expect(progress.progressSummary).toBe("Generating music");
diff --git a/src/agents/tools/music-generate-tool.ts b/src/agents/tools/music-generate-tool.ts
index cf14cd01d0e..1cde08a1917 100644
--- a/src/agents/tools/music-generate-tool.ts
+++ b/src/agents/tools/music-generate-tool.ts
@@ -45,7 +45,6 @@ import {
hasGenerationToolAvailability,
normalizeMediaReferenceInputs,
readBooleanToolParam,
- readGenerationTimeoutMs,
resolveCapabilityModelConfigForTool,
resolveGenerateAction,
resolveMediaToolLocalRoots,
@@ -82,7 +81,7 @@ const log = createSubsystemLogger("agents/tools/music-generate");
const MAX_INPUT_IMAGES = 10;
const SUPPORTED_OUTPUT_FORMATS = new Set(["mp3", "wav"]);
const DEFAULT_REFERENCE_FETCH_TIMEOUT_MS = 30_000;
-const MIN_MUSIC_GENERATION_TIMEOUT_MS = 10_000;
+const MIN_MUSIC_GENERATION_TIMEOUT_MS = 120_000;
const MusicGenerateToolSchema = Type.Object({
action: Type.Optional(
@@ -123,13 +122,6 @@ const MusicGenerateToolSchema = Type.Object({
minimum: 1,
}),
),
- timeoutMs: Type.Optional(
- Type.Number({
- description:
- "Optional provider request timeout in milliseconds. Values below 10000ms are raised to 10000ms.",
- minimum: 1,
- }),
- ),
format: Type.Optional(
Type.String({
description: 'Optional output format hint: "mp3" or "wav" when the provider supports it.',
@@ -653,12 +645,8 @@ export function createMusicGenerateTool(options?: {
});
const format = normalizeOutputFormat(readStringParam(args, "format"));
const filename = readStringParam(args, "filename");
- const requestedTimeoutMs = readGenerationTimeoutMs(args);
- const requestedGenerationTimeoutMs =
- requestedTimeoutMs ?? musicGenerationModelConfig.timeoutMs;
- const timeout = normalizeMusicGenerationTimeoutMs(requestedGenerationTimeoutMs);
+ const timeout = normalizeMusicGenerationTimeoutMs(musicGenerationModelConfig.timeoutMs);
const timeoutMs = timeout.timeoutMs;
- const referenceFetchTimeoutMs = requestedTimeoutMs === undefined ? undefined : timeoutMs;
const imageInputs = normalizeReferenceImageInputs(args);
const selectedModelRef =
parseMusicGenerationModelRef(model) ??
@@ -677,7 +665,6 @@ export function createMusicGenerateTool(options?: {
workspaceDir: options?.workspaceDir,
sandboxConfig,
ssrfPolicy: remoteMediaSsrfPolicy,
- timeoutMs: referenceFetchTimeoutMs,
});
validateMusicGenerationCapabilities({
provider: selectedProvider,