Skip to content

fix(terminal): tolerate undefined path in formatDocsLink#67086

Merged
pgondhi987 merged 1 commit into
openclaw:mainfrom
hclsys:fix/format-docs-link-undefined-67076
Apr 15, 2026
Merged

fix(terminal): tolerate undefined path in formatDocsLink#67086
pgondhi987 merged 1 commit into
openclaw:mainfrom
hclsys:fix/format-docs-link-undefined-67076

Conversation

@hclsys

@hclsys hclsys commented Apr 15, 2026

Copy link
Copy Markdown

What

Widen formatDocsLink(path, ...) in src/terminal/links.ts to accept string | undefined | null and fall back to the docs root (https://docs.openclaw.ai) when path is missing or whitespace-only.

Why

Fixes #67076 and #67074.

formatDocsLink called path.trim() unconditionally. The typed contract says ChannelMeta.docsPath: string (required), but a handful of channel plugins / catalog rows leave it unset at runtime. When the onboarding flow calls formatChannelSelectionLine(entry.meta, formatDocsLink) in src/flows/channel-setup.status.ts:233 for every channel, the first entry whose meta.docsPath is undefined throws:

TypeError: Cannot read properties of undefined (reading 'trim')
  at formatDocsLink (.../links-BlUEqVE8.js:7)
  at formatChannelSelectionLine
  at resolveChannelSelectionNoteLines

Reproduction paths from the two reports:

  • Select Discord → bot token → any channel option (macOS/Ubuntu, 4.14)
  • Select channel (QuickStart)Skip for now (Windows, 4.12/4.14)

Root cause confirmed by @Kovisun in #67074: the crash is inside formatDocsLink because at least one meta in the selection pass lacks docsPath.

Why defensive at this site (vs hunting every plugin that drops docsPath)

  • formatDocsLink is the single chokepoint shared by every plugin-sdk formatDocsLink re-export (nextcloud-talk, tlon, twitch, bluebubbles, msteams, feishu, matrix, irc, googlechat, etc.) and by the onboarding + setup flows.
  • A defensive change here fixes two separate user-visible crash reports with one code site and no behavioral change for callers that already pass a string.
  • The typed contract stays honest for the guarded callers (optional-channel-setup.ts already wraps in if (params.docsPath)) and no-longer-crashes for unguarded ones (registry.ts:formatChannelSelectionLine).

Behavior

Input Before After
"/channels/telegram" https://docs.openclaw.ai/channels/telegram unchanged
"https://example.com/p" https://example.com/p unchanged
" " (whitespace) malformed URL https://docs.openclaw.ai/ (empty trimmed) https://docs.openclaw.ai
undefined crashTypeError https://docs.openclaw.ai
null crash https://docs.openclaw.ai

Test

Added src/terminal/links.test.ts with 5 cases covering the existing happy paths plus the two regression cases (undefined, null).

pnpm test -- src/terminal/links.test.ts
# Test Files  1 passed
# Tests       5 passed

Risk

Minimal. Single file, 16 lines changed, widens a permissive signature. Callers that still pass required strings see identical output. Callers that previously crashed now silently fall back to the docs root URL — the onboarding flow continues rather than wedging.

, openclaw#67074)

formatDocsLink called path.trim() unconditionally. The typed contract
says 'docsPath: string' (required on ChannelMeta), but a handful of
channel plugins and catalog rows leave it unset at runtime, so
onboarding flows that call formatChannelSelectionLine(entry.meta, ...)
hit a TypeError on the first meta without a docsPath:

  TypeError: Cannot read properties of undefined (reading 'trim')

Symptom: 'openclaw onboard --install-daemon' and the 'Select channel
(QuickStart)' -> 'Skip for now' path both crash on 2026.4.12 and
2026.4.14.

Fix: widen formatDocsLink's path parameter to 'string | undefined |
null' and fall back to the docs root when path is missing. The single
call site that guards with 'if (params.docsPath)' stays fine; the
unguarded channel-selection path now degrades gracefully.

Fixes openclaw#67076
Fixes openclaw#67074
@greptile-apps

greptile-apps Bot commented Apr 15, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Defensive fix for the formatDocsLink crash when meta.docsPath is unset at runtime. The function signature is widened to string | undefined | null and a falsy/whitespace-trimmed path now falls back to the docs root URL instead of throwing. All valid-string paths are unaffected.

Confidence Score: 5/5

Safe to merge — single-site defensive fix with no behavioral change for existing callers passing valid strings.

The change is minimal (one function, 16 lines), correctly handles all edge cases, and is well-tested. The only finding is a P2 style suggestion on test completeness that does not affect correctness or production behavior.

No files require special attention.

Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/terminal/links.test.ts
Line: 28-30

Comment:
**`null` test missing output assertion**

The `null` case only asserts no throw, while the `undefined` case also verifies the returned URL contains the docs root. For consistency and to catch future regressions (e.g. if the fallback accidentally returns `""` or `"null"`), add the same output check here.

```suggestion
  it("does not crash when path is null", () => {
    expect(() => formatDocsLink(null as unknown as string)).not.toThrow();
    const out = formatDocsLink(null as unknown as string);
    expect(out).toContain("https://docs.openclaw.ai");
  });
```

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix(terminal): tolerate undefined path i..." | Re-trigger Greptile

Comment on lines +28 to +30
it("does not crash when path is null", () => {
expect(() => formatDocsLink(null as unknown as string)).not.toThrow();
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 null test missing output assertion

The null case only asserts no throw, while the undefined case also verifies the returned URL contains the docs root. For consistency and to catch future regressions (e.g. if the fallback accidentally returns "" or "null"), add the same output check here.

Suggested change
it("does not crash when path is null", () => {
expect(() => formatDocsLink(null as unknown as string)).not.toThrow();
});
it("does not crash when path is null", () => {
expect(() => formatDocsLink(null as unknown as string)).not.toThrow();
const out = formatDocsLink(null as unknown as string);
expect(out).toContain("https://docs.openclaw.ai");
});
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/terminal/links.test.ts
Line: 28-30

Comment:
**`null` test missing output assertion**

The `null` case only asserts no throw, while the `undefined` case also verifies the returned URL contains the docs root. For consistency and to catch future regressions (e.g. if the fallback accidentally returns `""` or `"null"`), add the same output check here.

```suggestion
  it("does not crash when path is null", () => {
    expect(() => formatDocsLink(null as unknown as string)).not.toThrow();
    const out = formatDocsLink(null as unknown as string);
    expect(out).toContain("https://docs.openclaw.ai");
  });
```

How can I resolve this? If you propose a fix, please make it concise.

@pgondhi987
pgondhi987 merged commit be7f4a2 into openclaw:main Apr 15, 2026
45 checks passed
xudaiyanzi pushed a commit to xudaiyanzi/openclaw that referenced this pull request Apr 17, 2026
, openclaw#67074) (openclaw#67086)

formatDocsLink called path.trim() unconditionally. The typed contract
says 'docsPath: string' (required on ChannelMeta), but a handful of
channel plugins and catalog rows leave it unset at runtime, so
onboarding flows that call formatChannelSelectionLine(entry.meta, ...)
hit a TypeError on the first meta without a docsPath:

  TypeError: Cannot read properties of undefined (reading 'trim')

Symptom: 'openclaw onboard --install-daemon' and the 'Select channel
(QuickStart)' -> 'Skip for now' path both crash on 2026.4.12 and
2026.4.14.

Fix: widen formatDocsLink's path parameter to 'string | undefined |
null' and fall back to the docs root when path is missing. The single
call site that guards with 'if (params.docsPath)' stays fine; the
unguarded channel-selection path now degrades gracefully.

Fixes openclaw#67076
Fixes openclaw#67074
kvnkho pushed a commit to kvnkho/openclaw that referenced this pull request Apr 17, 2026
, openclaw#67074) (openclaw#67086)

formatDocsLink called path.trim() unconditionally. The typed contract
says 'docsPath: string' (required on ChannelMeta), but a handful of
channel plugins and catalog rows leave it unset at runtime, so
onboarding flows that call formatChannelSelectionLine(entry.meta, ...)
hit a TypeError on the first meta without a docsPath:

  TypeError: Cannot read properties of undefined (reading 'trim')

Symptom: 'openclaw onboard --install-daemon' and the 'Select channel
(QuickStart)' -> 'Skip for now' path both crash on 2026.4.12 and
2026.4.14.

Fix: widen formatDocsLink's path parameter to 'string | undefined |
null' and fall back to the docs root when path is missing. The single
call site that guards with 'if (params.docsPath)' stays fine; the
unguarded channel-selection path now degrades gracefully.

Fixes openclaw#67076
Fixes openclaw#67074
@clawsweeper clawsweeper Bot mentioned this pull request Apr 27, 2026
25 tasks
lovewanwan pushed a commit to lovewanwan/openclaw that referenced this pull request Apr 28, 2026
, openclaw#67074) (openclaw#67086)

formatDocsLink called path.trim() unconditionally. The typed contract
says 'docsPath: string' (required on ChannelMeta), but a handful of
channel plugins and catalog rows leave it unset at runtime, so
onboarding flows that call formatChannelSelectionLine(entry.meta, ...)
hit a TypeError on the first meta without a docsPath:

  TypeError: Cannot read properties of undefined (reading 'trim')

Symptom: 'openclaw onboard --install-daemon' and the 'Select channel
(QuickStart)' -> 'Skip for now' path both crash on 2026.4.12 and
2026.4.14.

Fix: widen formatDocsLink's path parameter to 'string | undefined |
null' and fall back to the docs root when path is missing. The single
call site that guards with 'if (params.docsPath)' stays fine; the
unguarded channel-selection path now degrades gracefully.

Fixes openclaw#67076
Fixes openclaw#67074
ogt-redknie pushed a commit to ogt-redknie/OPENX that referenced this pull request May 2, 2026
, openclaw#67074) (openclaw#67086)

formatDocsLink called path.trim() unconditionally. The typed contract
says 'docsPath: string' (required on ChannelMeta), but a handful of
channel plugins and catalog rows leave it unset at runtime, so
onboarding flows that call formatChannelSelectionLine(entry.meta, ...)
hit a TypeError on the first meta without a docsPath:

  TypeError: Cannot read properties of undefined (reading 'trim')

Symptom: 'openclaw onboard --install-daemon' and the 'Select channel
(QuickStart)' -> 'Skip for now' path both crash on 2026.4.12 and
2026.4.14.

Fix: widen formatDocsLink's path parameter to 'string | undefined |
null' and fall back to the docs root when path is missing. The single
call site that guards with 'if (params.docsPath)' stays fine; the
unguarded channel-selection path now degrades gracefully.

Fixes openclaw#67076
Fixes openclaw#67074
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 9, 2026
, openclaw#67074) (openclaw#67086)

formatDocsLink called path.trim() unconditionally. The typed contract
says 'docsPath: string' (required on ChannelMeta), but a handful of
channel plugins and catalog rows leave it unset at runtime, so
onboarding flows that call formatChannelSelectionLine(entry.meta, ...)
hit a TypeError on the first meta without a docsPath:

  TypeError: Cannot read properties of undefined (reading 'trim')

Symptom: 'openclaw onboard --install-daemon' and the 'Select channel
(QuickStart)' -> 'Skip for now' path both crash on 2026.4.12 and
2026.4.14.

Fix: widen formatDocsLink's path parameter to 'string | undefined |
null' and fall back to the docs root when path is missing. The single
call site that guards with 'if (params.docsPath)' stays fine; the
unguarded channel-selection path now degrades gracefully.

Fixes openclaw#67076
Fixes openclaw#67074
globalcaos pushed a commit to globalcaos/tinkerclaw that referenced this pull request May 13, 2026
, openclaw#67074) (openclaw#67086)

formatDocsLink called path.trim() unconditionally. The typed contract
says 'docsPath: string' (required on ChannelMeta), but a handful of
channel plugins and catalog rows leave it unset at runtime, so
onboarding flows that call formatChannelSelectionLine(entry.meta, ...)
hit a TypeError on the first meta without a docsPath:

  TypeError: Cannot read properties of undefined (reading 'trim')

Symptom: 'openclaw onboard --install-daemon' and the 'Select channel
(QuickStart)' -> 'Skip for now' path both crash on 2026.4.12 and
2026.4.14.

Fix: widen formatDocsLink's path parameter to 'string | undefined |
null' and fall back to the docs root when path is missing. The single
call site that guards with 'if (params.docsPath)' stays fine; the
unguarded channel-selection path now degrades gracefully.

Fixes openclaw#67076
Fixes openclaw#67074
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 24, 2026
, openclaw#67074) (openclaw#67086)

formatDocsLink called path.trim() unconditionally. The typed contract
says 'docsPath: string' (required on ChannelMeta), but a handful of
channel plugins and catalog rows leave it unset at runtime, so
onboarding flows that call formatChannelSelectionLine(entry.meta, ...)
hit a TypeError on the first meta without a docsPath:

  TypeError: Cannot read properties of undefined (reading 'trim')

Symptom: 'openclaw onboard --install-daemon' and the 'Select channel
(QuickStart)' -> 'Skip for now' path both crash on 2026.4.12 and
2026.4.14.

Fix: widen formatDocsLink's path parameter to 'string | undefined |
null' and fall back to the docs root when path is missing. The single
call site that guards with 'if (params.docsPath)' stays fine; the
unguarded channel-selection path now degrades gracefully.

Fixes openclaw#67076
Fixes openclaw#67074
jameslcowan pushed a commit to jameslcowan/openclaw that referenced this pull request Jun 2, 2026
, openclaw#67074) (openclaw#67086)

formatDocsLink called path.trim() unconditionally. The typed contract
says 'docsPath: string' (required on ChannelMeta), but a handful of
channel plugins and catalog rows leave it unset at runtime, so
onboarding flows that call formatChannelSelectionLine(entry.meta, ...)
hit a TypeError on the first meta without a docsPath:

  TypeError: Cannot read properties of undefined (reading 'trim')

Symptom: 'openclaw onboard --install-daemon' and the 'Select channel
(QuickStart)' -> 'Skip for now' path both crash on 2026.4.12 and
2026.4.14.

Fix: widen formatDocsLink's path parameter to 'string | undefined |
null' and fall back to the docs root when path is missing. The single
call site that guards with 'if (params.docsPath)' stays fine; the
unguarded channel-selection path now degrades gracefully.

Fixes openclaw#67076
Fixes openclaw#67074
@clawsweeper clawsweeper Bot mentioned this pull request Jun 17, 2026
25 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Cant read properties of undefined (reading 'trim')

2 participants