{"record":{"id":"1c0eb3433b70b336","repo":"sveltejs/kit","slug":"keypath-should-be-a-function-if-specified","errorCode":null,"errorMessage":"${keypath} should be a function, if specified","messagePattern":"(.+?) should be a function, if specified","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/kit/src/core/config/options.js","lineNumber":500,"sourceCode":"\t\t\t// prettier-ignore\n\t\t\tconst msg = options.length > 2\n\t\t\t\t? `${keypath} should be one of ${options.slice(0, -1).map(input => `\"${input}\"`).join(', ')} or \"${options[options.length - 1]}\"`\n\t\t\t\t: `${keypath} should be either \"${options[0]}\" or \"${options[1]}\"`;\n\n\t\t\tthrow new Error(msg);\n\t\t}\n\t\treturn input;\n\t});\n}\n\n/**\n * @param {(...args: any) => any} fallback\n * @returns {Validator}\n */\nfunction fun(fallback) {\n\treturn validate(fallback, (input, keypath) => {\n\t\tif (typeof input !== 'function') {\n\t\t\tthrow new Error(`${keypath} should be a function, if specified`);\n\t\t}\n\t\treturn input;\n\t});\n}\n\nfunction any() {\n\treturn validate(undefined, (input) => input);\n}\n\n/**\n * @param {string} input\n * @param {string} keypath\n */\nfunction assert_string(input, keypath) {\n\tif (typeof input !== 'string') {\n\t\tthrow new Error(`${keypath} should be a string, if specified`);\n\t}\n}","sourceCodeStart":482,"sourceCodeEnd":518,"githubUrl":"https://github.com/sveltejs/kit/blob/03f1687fe612ce3d2d9131139b5b188d9cf90c64/packages/kit/src/core/config/options.js#L482-L518","documentation":"The `fun()` validator checks that an optional config option, when provided, is a function. If a non-function value is assigned to a hook or callback option (e.g. handle, handleError), SvelteKit throws this error at config load time.","triggerScenarios":"Assigning a non-function to a function-typed kit config option, e.g. `handle: './hooks'` (string) or `handle: myMiddleware()` (invoked result) instead of `handle: myMiddleware`.","commonSituations":"Accidentally invoking the hook function instead of passing it; importing the wrong export from hooks.server.js; passing an async wrapper incorrectly or forgetting the export in a re-export chain.","solutions":["Open svelte.config.js and locate the option named in the keypath.","Ensure the value is a function reference, not a call result or string.","Import the hook correctly: `handle: import('./hooks.server').then(m => m.handle)` is not allowed — use a direct function import or define it inline."],"exampleFix":"// before\nkit: { handle: './src/hooks.server.js' }\n// after\nimport handle from './src/hooks.server.js';\nconst config = { kit: { handle } };","handlingStrategy":"type-guard","validationCode":"if (config.kit?.handle !== undefined && typeof config.kit.handle !== 'function') {\n  throw new Error('kit.handle must be a function');\n}","typeGuard":"function isFunction(v) {\n  return typeof v === 'function';\n}","tryCatchPattern":"try {\n  build(config);\n} catch (e) {\n  if (String(e.message).includes('should be a function')) {\n    console.error('Config hook is not a function — check for accidental invocation: use fn, not fn()');\n  } else throw e;\n}","preventionTips":["Never append () when assigning hook functions in svelte.config.js.","Verify the hook module's named export is a function before re-exporting it.","Use JSDoc or types (import type { Handle }) so the editor flags non-function assignments."],"tags":["config","validation","typescript"],"backgroundTag":"invalid-config-option-value","analyzedSha":"03f1687fe612ce3d2d9131139b5b188d9cf90c64","analyzedAt":"2026-09-02T02:01:50.504Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T06:17:21.866Z"}