fix(terminal): tolerate undefined path in formatDocsLink#67086
Conversation
, 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 SummaryDefensive fix for the Confidence Score: 5/5Safe 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 AIThis 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 |
| it("does not crash when path is null", () => { | ||
| expect(() => formatDocsLink(null as unknown as string)).not.toThrow(); | ||
| }); |
There was a problem hiding this 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.
| 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., 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
, 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
, 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
, 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
, 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
, 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
, 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
, 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
What
Widen
formatDocsLink(path, ...)insrc/terminal/links.tsto acceptstring | undefined | nulland fall back to the docs root (https://docs.openclaw.ai) whenpathis missing or whitespace-only.Why
Fixes #67076 and #67074.
formatDocsLinkcalledpath.trim()unconditionally. The typed contract saysChannelMeta.docsPath: string(required), but a handful of channel plugins / catalog rows leave it unset at runtime. When the onboarding flow callsformatChannelSelectionLine(entry.meta, formatDocsLink)insrc/flows/channel-setup.status.ts:233for every channel, the first entry whosemeta.docsPathis undefined throws:Reproduction paths from the two reports:
Select channel (QuickStart)→Skip for now(Windows, 4.12/4.14)Root cause confirmed by @Kovisun in #67074: the crash is inside
formatDocsLinkbecause at least one meta in the selection pass lacksdocsPath.Why defensive at this site (vs hunting every plugin that drops docsPath)
formatDocsLinkis the single chokepoint shared by every plugin-sdkformatDocsLinkre-export (nextcloud-talk,tlon,twitch,bluebubbles,msteams,feishu,matrix,irc,googlechat, etc.) and by the onboarding + setup flows.optional-channel-setup.tsalready wraps inif (params.docsPath)) and no-longer-crashes for unguarded ones (registry.ts:formatChannelSelectionLine).Behavior
"/channels/telegram"https://docs.openclaw.ai/channels/telegram"https://example.com/p"https://example.com/p" "(whitespace)https://docs.openclaw.ai/(empty trimmed)https://docs.openclaw.aiundefinedTypeErrorhttps://docs.openclaw.ainullhttps://docs.openclaw.aiTest
Added
src/terminal/links.test.tswith 5 cases covering the existing happy paths plus the two regression cases (undefined,null).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.