openclaw - 💡(How to fix) Fix GPT-5.4 default parameter values break message tool (poll, components, media path) [1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#52757Fetched 2026-04-08 01:19:50
View on GitHub
Comments
1
Participants
2
Timeline
2
Reactions
0
Author
Timeline (top)
commented ×1subscribed ×1

When using GPT-5.4 as the model provider, the message tool fails in multiple ways because GPT-5.4 sends all tool parameters with default values (e.g. pollDurationHours: 24, pollMulti: false, components: {blocks:[], modal:{fields:[],...}, ...}), unlike other models that omit unused optional parameters.

This causes three cascading issues:

Error Message

if (action === "send" && hasPollCreationParams(params)) throw new Error("Poll fields require action "poll"..."); if (action === "send" && typeof (readSnakeCaseParamRaw(params, "pollQuestion") ?? "") === "string" && (readSnakeCaseParamRaw(params, "pollQuestion") ?? "").toString().trim().length > 0) throw new Error("Poll fields require action "poll"...");

  1. components.modal.fields must be a non-empty array error (empty fields: [] fails validation)

Root Cause

When using GPT-5.4 as the model provider, the message tool fails in multiple ways because GPT-5.4 sends all tool parameters with default values (e.g. pollDurationHours: 24, pollMulti: false, components: {blocks:[], modal:{fields:[],...}, ...}), unlike other models that omit unused optional parameters.

Fix Action

Fix / Workaround

Rather than patching each validation individually, consider a general approach: strip/normalize default-valued optional parameters at the tool input boundary before validation, so that pollDurationHours: 24 (default), components: {blocks:[]} (empty), etc. are treated the same as omitted parameters regardless of which model is calling the tool.

Code Example

// Before
if (action === "send" && hasPollCreationParams(params)) throw new Error("Poll fields require action \"poll\"...");

// After
if (action === "send" && typeof (readSnakeCaseParamRaw(params, "pollQuestion") ?? "") === "string" && (readSnakeCaseParamRaw(params, "pollQuestion") ?? "").toString().trim().length > 0) throw new Error("Poll fields require action \"poll\"...");

---

function readDiscordComponentSpec(raw) {
    if (raw === void 0 || raw === null) return null;
    // Skip empty default components from models that fill all params
    if (typeof raw === "object" && raw !== null) {
        const b = raw.blocks; const m = raw.modal; const t = raw.text;
        const blocksEmpty = !b || (Array.isArray(b) && b.length === 0);
        const modalEmpty = !m || (typeof m === "object" && Array.isArray(m.fields) && m.fields.length === 0);
        const textEmpty = !t || (typeof t === "string" && t.trim() === "");
        if (blocksEmpty && modalEmpty && textEmpty) return null;
    }
    // ... rest of function
}
RAW_BUFFERClick to expand / collapse

Summary

When using GPT-5.4 as the model provider, the message tool fails in multiple ways because GPT-5.4 sends all tool parameters with default values (e.g. pollDurationHours: 24, pollMulti: false, components: {blocks:[], modal:{fields:[],...}, ...}), unlike other models that omit unused optional parameters.

This causes three cascading issues:

Bug 1: Poll validation false positive

hasPollCreationParams() detects pollDurationHours: 24 and pollMulti: false as poll intent, throwing:

Poll fields require action "poll"; use action "poll" instead of "send".

Root cause: The guard checks for the presence of any poll-related parameter with a non-empty value, but GPT-5.4 always sends pollDurationHours: 24 (the default) even for non-poll messages.

Fix: Only trigger the poll guard when pollQuestion is a non-empty string, since a poll without a question is never intentional:

// Before
if (action === "send" && hasPollCreationParams(params)) throw new Error("Poll fields require action \"poll\"...");

// After
if (action === "send" && typeof (readSnakeCaseParamRaw(params, "pollQuestion") ?? "") === "string" && (readSnakeCaseParamRaw(params, "pollQuestion") ?? "").toString().trim().length > 0) throw new Error("Poll fields require action \"poll\"...");

Bug 2: Components empty defaults route to wrong send path

GPT-5.4 sends components: {blocks:[], modal:{fields:[], title:"", ...}, reusable:false, text:""}. readDiscordComponentSpec() returns a non-null object, causing the message to go through the Discord Components v2 send path (sendDiscordComponentMessage) instead of the normal sendMessageDiscord path. This results in:

  1. components.modal.fields must be a non-empty array error (empty fields: [] fails validation)
  2. Even after bypassing the modal validation, file attachments are silently dropped because the Components v2 path doesn't handle them the same way

Fix: Return null from readDiscordComponentSpec when all fields are empty defaults:

function readDiscordComponentSpec(raw) {
    if (raw === void 0 || raw === null) return null;
    // Skip empty default components from models that fill all params
    if (typeof raw === "object" && raw !== null) {
        const b = raw.blocks; const m = raw.modal; const t = raw.text;
        const blocksEmpty = !b || (Array.isArray(b) && b.length === 0);
        const modalEmpty = !m || (typeof m === "object" && Array.isArray(m.fields) && m.fields.length === 0);
        const textEmpty = !t || (typeof t === "string" && t.trim() === "");
        if (blocksEmpty && modalEmpty && textEmpty) return null;
    }
    // ... rest of function
}

Bug 3: workspace- media path restriction

assertLocalMediaAllowed() blocks file paths under workspace-* directories (e.g. workspace-team/), but agents commonly generate output files in their workspace. This causes:

Local media path is not under an allowed directory: /home/ubuntu/.openclaw/workspace-team/edited_schedule.pdf

Suggestion: Consider allowing workspace directories in the media path whitelist, or provide a configuration option (e.g. localRoots: "any" in config) to relax this restriction.

Environment

  • OpenClaw version: 2026.3.13
  • Model: GPT-5.4 (via hunters provider)
  • Agent: content-creator (specialist)
  • Platform: Discord gateway

Suggested general fix

Rather than patching each validation individually, consider a general approach: strip/normalize default-valued optional parameters at the tool input boundary before validation, so that pollDurationHours: 24 (default), components: {blocks:[]} (empty), etc. are treated the same as omitted parameters regardless of which model is calling the tool.

extent analysis

Fix Plan

To address the issues caused by GPT-5.4 sending all tool parameters with default values, we will implement the following fixes:

  • Bug 1: Poll validation false positive
    • Modify the hasPollCreationParams check to only trigger when pollQuestion is a non-empty string.
    • Example code:

if (action === "send" && typeof (readSnakeCaseParamRaw(params, "pollQuestion") ?? "") === "string" && (readSnakeCaseParamRaw(params, "pollQuestion") ?? "").toString().trim().length > 0) { throw new Error("Poll fields require action "poll"..."); }


* **Bug 2: Components empty defaults route to wrong send path**
  + Update the `readDiscordComponentSpec` function to return `null` when all fields are empty defaults.
  + Example code:
    ```javascript
function readDiscordComponentSpec(raw) {
  if (raw === void 0 || raw === null) return null;
  if (typeof raw === "object" && raw !== null) {
    const b = raw.blocks; const m = raw.modal; const t = raw.text;
    const blocksEmpty = !b || (Array.isArray(b) && b.length === 0);
    const modalEmpty = !m || (typeof m === "object" && Array.isArray(m.fields) && m.fields.length === 0);
    const textEmpty = !t || (typeof t === "string" && t.trim() === "");
    if (blocksEmpty && modalEmpty && textEmpty) return null;
  }
  // ... rest of function
}
  • Bug 3: workspace- media path restriction
    • Consider adding a configuration option (e.g. localRoots: "any") to relax the media path restriction.
    • Alternatively, update the assertLocalMediaAllowed function to allow workspace directories in the media path whitelist.

Verification

To verify that the fixes work, test the following scenarios:

  • Send a message with pollDurationHours: 24 but no pollQuestion to ensure the poll validation false positive is resolved.
  • Send a message with empty components (e.g. components: {blocks:[], modal:{fields:[], title:"", ...}, reusable:false, text:""}) to ensure the correct send path is used.
  • Send a message with a file attachment from a workspace- directory to ensure the media path restriction is relaxed.

Extra Tips

To prevent similar issues in the future, consider implementing a general approach to strip/normalize default-valued optional parameters at the tool input boundary before validation. This will ensure that parameters like pollDurationHours: 24 and components: {blocks:[]} are treated the same as omitted parameters, regardless of the model calling the tool.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - 💡(How to fix) Fix GPT-5.4 default parameter values break message tool (poll, components, media path) [1 comments, 2 participants]