windmill-labs/windmill · error

resource ${path} has no string "content" — is it an ${SKILLS

Error message

resource ${path} has no string "content" — is it an ${SKILLS_RESOURCE_TYPE}?

What it means

readSkillBody fetches a Windmill resource value and expects it to be a skill resource: an object with a string `content` field holding the SKILL.md body. If the resource is missing, of a different resource type, or its value lacks a string content field, it throws this error rather than returning '' — an empty string would reach the model as 'a skill with no instructions', which the agent would then wrongly act on.

Source

Thrown at frontend/src/lib/components/copilot/chat/skills/skillResources.ts:118

	// `fatal: false` replaces the partial code point a byte-aligned cut can leave
	// with U+FFFD; dropping it keeps the tail clean.
	const cut = new TextDecoder('utf-8').decode(encoded.slice(0, maxBytes)).replace(/�$/, '')
	return `${cut}… [truncated]`
}

/** The SKILL.md body of one skill. Throws rather than returning `''` when the
 * resource holds no readable body: an empty string reaches the model as a
 * successful read of a skill with no instructions, which it would then act on.
 *
 * Deliberately unbounded — the editor loads through here and saves what it loaded,
 * so truncating would rewrite an over-long skill the first time someone opened it.
 * Bounding belongs at the prompt boundary, where the cost actually is. */
export async function readSkillBody(workspace: string, path: string): Promise<string> {
	const value = (await ResourceService.getResourceValue({ workspace, path })) as
		| { content?: unknown }
		| undefined
	if (typeof value?.content !== 'string') {
		throw new Error(`resource ${path} has no string "content" — is it an ${SKILLS_RESOURCE_TYPE}?`)
	}
	return value.content
}

export async function saveSkillResource(
	workspace: string,
	path: string,
	description: string,
	instructions: string,
	{ overwrite = false }: { overwrite?: boolean } = {}
): Promise<void> {
	await ResourceService.createResource({
		workspace,
		updateIfExists: overwrite,
		requestBody: {
			path,
			description,
			value: { content: instructions },

View on GitHub (pinned to e474e8803c)

Solutions

  1. Verify the resource exists at `path` and is of the skill resource type
  2. Re-create or repair the skill so its value is {content: "<SKILL.md body>"} (saveSkillResource does this)
  3. If it is a leftover/broken resource, delete it and re-list skills before reading
  4. Wrap the read in a try/catch when loading skill instructions so the agent gets a readable message instead of a crash

Example fix

// before
const body = await readSkillBody(ws, 'u/admin/skills/deploy')
// after
let body
try {
  body = await readSkillBody(ws, 'u/admin/skills/deploy')
} catch {
  body = 'Skill body unavailable: resource is not a valid skill resource.'
}
Defensive patterns

Strategy: type-guard

Validate before calling

const res = await ResourceService.getResourceValue({ workspace, path })
const ok = !!res && typeof (res as any).content === 'string'
if (!ok) throw new Error(`${path} is not a valid skill resource`)

Type guard

function isSkillResource(v: unknown): v is { content: string } {
  return typeof v === 'object' && v !== null && typeof (v as any).content === 'string'
}

Try / catch

let body: string
try {
  body = await readSkillBody(ws, path)
} catch (e) {
  body = `Skill unavailable: ${(e as Error).message}`
}

Prevention

When it happens

Trigger: ResourceService.getResourceValue returns undefined (resource deleted or 404 path), or returns a value whose `content` is a non-string (object, number) — e.g. the resource at the given path is not of SKILLS_RESOURCE_TYPE or was created with a different value shape.

Common situations: A skill resource was edited by hand in the resource editor and its value no longer has {content: "..."}; the path passed by the agent points at an unrelated resource; the skills resource type doesn't exist in a fresh instance so the read resolves to nothing; stale skill listing after deletion.

Related errors


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/9197e705a7d4b0a3. Report an issue: GitHub.